├── .editorconfig
├── t3n-neos-debug.jpg
├── e2e
├── .gitignore
├── cypress.json
├── cypress
│ ├── plugins
│ │ └── index.js
│ ├── support
│ │ ├── index.js
│ │ └── commands.js
│ ├── tsconfig.json
│ └── integration
│ │ └── debug-console
│ │ ├── cacheModule.ts
│ │ ├── sqlModule.ts
│ │ └── usage.ts
├── Settings.yaml
├── package.json
└── package-lock.json
├── server-timing-example.jpg
├── .dependabot
└── config.yml
├── phpcs.xml.dist
├── composer.json
├── Configuration
└── Settings.yaml
├── composer.json.ci
├── LICENSE
├── Resources
├── Private
│ └── Fusion
│ │ └── Root.fusion
└── Public
│ ├── Style
│ └── main.css
│ └── Script
│ └── main.js
├── Classes
├── Aspect
│ ├── RuntimeTracingAspect.php
│ ├── CollectDebugInformationAspect.php
│ └── ContentCacheSegmentAspect.php
├── Http
│ └── Middleware
│ │ ├── MeasureServerTimingMiddleware.php
│ │ └── AddServerTimingMiddleware.php
├── Service
│ ├── RenderTimer.php
│ └── DebugService.php
└── Logging
│ └── DebugStack.php
├── README.md
└── .circleci
└── config.yml
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*.js]
4 | indent_size = 2
5 |
--------------------------------------------------------------------------------
/t3n-neos-debug.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/t3n/neos-debug/HEAD/t3n-neos-debug.jpg
--------------------------------------------------------------------------------
/e2e/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | build
4 | cypress/videos
5 | cypress/screenshots
6 |
--------------------------------------------------------------------------------
/server-timing-example.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/t3n/neos-debug/HEAD/server-timing-example.jpg
--------------------------------------------------------------------------------
/e2e/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "baseUrl": "http://127.0.0.1:8081",
3 | "viewportWidth": 1200,
4 | "viewportHeight": 1000
5 | }
6 |
--------------------------------------------------------------------------------
/e2e/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | // `on` is used to hook into various events Cypress emits
3 | // `config` is the resolved Cypress config
4 | };
5 |
--------------------------------------------------------------------------------
/e2e/cypress/support/index.js:
--------------------------------------------------------------------------------
1 | //
2 | //
3 |
4 | import "@testing-library/cypress/add-commands";
5 | import "./commands";
6 |
--------------------------------------------------------------------------------
/e2e/cypress/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "baseUrl": "../node_modules",
5 | "target": "es5",
6 | "lib": ["es5", "dom"],
7 | "types": ["cypress", "@types/testing-library__cypress"]
8 | },
9 | "include": ["**/*.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/e2e/Settings.yaml:
--------------------------------------------------------------------------------
1 | Neos:
2 | Flow:
3 | persistence:
4 | backendOptions:
5 | driver: 'pdo_mysql'
6 | dbname: 'neos-debug'
7 | password: 'some-password'
8 | user: root
9 | host: 127.0.0.1
10 |
11 | t3n:
12 | Neos:
13 | Debug:
14 | enabled: true
15 |
--------------------------------------------------------------------------------
/.dependabot/config.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | update_configs:
3 | - package_manager: "php:composer"
4 | directory: "/"
5 | update_schedule: "weekly"
6 | target_branch: "master"
7 | - package_manager: "javascript"
8 | directory: "/e2e"
9 | update_schedule: "weekly"
10 | target_branch: "master"
11 |
--------------------------------------------------------------------------------
/e2e/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | //
2 | //
3 |
4 | Cypress.Commands.add('openConsole', (setCookie = false) => {
5 | cy.window()
6 | .should('have.property', '__enable_neos_debug__')
7 | .then(debugStartupScript => debugStartupScript(setCookie));
8 | });
9 |
--------------------------------------------------------------------------------
/phpcs.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Classes
13 |
14 |
15 |
--------------------------------------------------------------------------------
/e2e/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "t3n-neos-debug-e2e",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "cy": "cypress run",
8 | "cy:open": "cypress open"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "devDependencies": {
13 | "@testing-library/cypress": "^5.0.2",
14 | "cypress": "^3.8.3",
15 | "eslint": "^6.8.0",
16 | "eslint-config-cypress": "^0.28.0",
17 | "typescript": "^3.7.5"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/e2e/cypress/integration/debug-console/cacheModule.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | describe('cache module can be used', () => {
5 | it('sql module can be used will bring up', () => {
6 | // the console is not there yet+
7 | cy.visit('/').openConsole();
8 | cy.queryByText(/cache \(/i)
9 | .click()
10 | .queryByText(/cache information/i)
11 | .should('exist')
12 | .queryByText(/cache \(/i)
13 | .click()
14 | .queryByText(/cache information/i)
15 | .should('not.exist');
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/e2e/cypress/integration/debug-console/sqlModule.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | describe('sql module can be used', () => {
5 | it('sql module can be used will bring up', () => {
6 | // the console is not there yet+
7 | cy.visit('/').openConsole();
8 | cy.queryByText(/sql \(/i)
9 | .click()
10 | .queryByText(/sql information/i)
11 | .should('exist')
12 | .queryByText(/Slow Queries:/i)
13 | .should('exist')
14 | .queryByText(/sql \(/i)
15 | .click()
16 | .queryByText(/sql information/i)
17 | .should('not.exist');
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "t3n/neos-debug",
3 | "description": "Helper package to visualize debug inforamations for neos including fusion content cache",
4 | "license": "MIT",
5 | "type": "neos-package",
6 | "abandoned": "Flowpack/neos-debug",
7 | "require": {
8 | "neos/neos": "^7.0 || ^8.0"
9 | },
10 | "autoload": {
11 | "psr-4": {
12 | "t3n\\Neos\\Debug\\": "Classes/"
13 | }
14 | },
15 | "extra": {
16 | "neos": {
17 | "package-key": "t3n.Neos.Debug",
18 | "loading-order": {
19 | "after": [ "neos/neos" ]
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Configuration/Settings.yaml:
--------------------------------------------------------------------------------
1 | t3n:
2 | Neos:
3 | Debug:
4 | enabled: false
5 | htmlOutput:
6 | enabled: true
7 | serverTimingHeader:
8 | enabled: false
9 | sql:
10 | # Set when a query should be considered as slow query. In ms
11 | slowQueryAfter: 10
12 |
13 | Neos:
14 | Neos:
15 | fusion:
16 | autoInclude:
17 | 't3n.Neos.Debug': true
18 |
19 | Flow:
20 | http:
21 | middlewares:
22 | t3n.Neos.Debug:MeasureServerTiming:
23 | position: 'start 999'
24 | middleware: 't3n\Neos\Debug\Http\Middleware\MeasureServerTimingMiddleware'
25 | t3n.Neos.Debug:AddServerTimingHeader:
26 | position: 'before dispatch 999'
27 | middleware: 't3n\Neos\Debug\Http\Middleware\AddServerTimingMiddleware'
28 |
29 | reflection:
30 | ignoredTags:
31 | 'phpcsSuppress': true
32 |
--------------------------------------------------------------------------------
/composer.json.ci:
--------------------------------------------------------------------------------
1 | {
2 | "name": "t3n/test-setup",
3 | "description": "Test setup for flow packages",
4 | "config": {
5 | "vendor-dir": "Packages/Libraries",
6 | "bin-dir": "bin"
7 | },
8 | "require": {
9 | "neos/neos": "^5.1",
10 | "neos/nodetypes": "^5.1",
11 | "neos/demo": "^6.1",
12 | "neos/buildessentials": "^6.1",
13 | "t3n/neos-debug": "@dev",
14 | "t3n/coding-standard": "~1.1.0"
15 | },
16 | "require-dev": {
17 | "squizlabs/php_codesniffer": "~3.5",
18 | "phpunit/phpunit": "~8.5",
19 | "mikey179/vfsstream": "~1.6"
20 | },
21 | "repositories": {
22 | "srcPackage": {
23 | "type": "path",
24 | "url": "./neos-debug"
25 | }
26 | },
27 | "scripts": {
28 | "post-update-cmd": "Neos\\Flow\\Composer\\InstallerScripts::postUpdateAndInstall",
29 | "post-install-cmd": "Neos\\Flow\\Composer\\InstallerScripts::postUpdateAndInstall",
30 | "post-package-update": "Neos\\Flow\\Composer\\InstallerScripts::postPackageUpdateAndInstall",
31 | "post-package-install": "Neos\\Flow\\Composer\\InstallerScripts::postPackageUpdateAndInstall"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 yeebase media GmbH
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 |
--------------------------------------------------------------------------------
/Resources/Private/Fusion/Root.fusion:
--------------------------------------------------------------------------------
1 |
2 | prototype(t3n.Neos.Debug:Styles) < prototype(Neos.Fusion:Tag) {
3 | tagName = 'link'
4 | attributes {
5 | rel = 'stylesheet'
6 | href = Neos.Fusion:ResourceUri {
7 | path = 'resource://t3n.Neos.Debug/Public/Style/main.css'
8 | }
9 | }
10 |
11 | @if.notInBackend = ${!documentNode.context.inBackend}
12 | @if.isActive = ${Configuration.setting('t3n.Neos.Debug.enabled') && Configuration.setting('t3n.Neos.Debug.htmlOutput.enabled')}
13 | }
14 |
15 | prototype(t3n.Neos.Debug:JavaScript) < prototype(Neos.Fusion:Tag) {
16 | tagName = 'script'
17 | attributes {
18 | src = Neos.Fusion:ResourceUri {
19 | path = 'resource://t3n.Neos.Debug/Public/Script/main.js'
20 | }
21 | }
22 |
23 | @if.notInBackend = ${!documentNode.context.inBackend}
24 | @if.isHtml = ${request.format == 'html'}
25 | @if.isActive = ${Configuration.setting('t3n.Neos.Debug.enabled') && Configuration.setting('t3n.Neos.Debug.htmlOutput.enabled')}
26 | }
27 |
28 | prototype(Neos.Neos:Page) {
29 | head.contentCacheDebugStyle = t3n.Neos.Debug:Styles {
30 | @position = 'end'
31 | }
32 | contentCacheDebugScript = t3n.Neos.Debug:JavaScript {
33 | @position = 'before closingBodyTag'
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Classes/Aspect/RuntimeTracingAspect.php:
--------------------------------------------------------------------------------
1 | enter()) && t3n\Neos\Debug\Aspect\RuntimeTracingAspect->debuggingActive")
43 | */
44 | public function onEnter(JoinPointInterface $joinPoint): void
45 | {
46 | $configuration = $joinPoint->getMethodArgument('configuration');
47 | $fusionPath = $joinPoint->getMethodArgument('fusionPath');
48 |
49 | $cacheMode = $configuration['mode'] ?? null;
50 |
51 | if (! $cacheMode) {
52 | return;
53 | }
54 |
55 | $this->renderTimer->start($fusionPath);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/e2e/cypress/integration/debug-console/usage.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | describe("debug console can be used at all", () => {
5 | it("console can be opened and closed", () => {
6 | // the console is not there yet+
7 | cy.visit("/")
8 | .queryByText(/cache \(/i)
9 | .should("not.exist");
10 |
11 | // bring up the console
12 | cy.openConsole()
13 | .queryByText(/cache \(/i)
14 | .should("exist")
15 | .queryByText(/close/i)
16 | .should("exist");
17 |
18 | // close it
19 | cy.window()
20 | .queryByText(/close/i)
21 | .click()
22 | .queryByText(/cache \(/i)
23 | .should("not.exist");
24 | });
25 |
26 | it("cookie can be set so console stays on a refresh", () => {
27 | cy.visit("/")
28 | .queryByText(/cache \(/i)
29 | .should("not.exist");
30 |
31 | // bring up the console
32 | cy.openConsole(true)
33 | .queryByText(/cache \(/i)
34 | .should("exist")
35 | .getCookie("__neos_debug__")
36 | .should("have.property", "value")
37 | .should("eq", "true");
38 | });
39 |
40 | it("debug console opens when the cookie is set", () => {
41 | cy.setCookie("__neos_debug__", "true")
42 | .visit("/")
43 | .queryByText(/cache \(/i)
44 | .should("exist");
45 | });
46 |
47 | it("closing the console will also delete the cooke", () => {
48 | cy.setCookie("__neos_debug__", "true")
49 | .visit("/")
50 | .queryByText(/close/i)
51 | .click()
52 | .getCookie("__neos_debug__")
53 | .should("eq", null);
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/Classes/Http/Middleware/MeasureServerTimingMiddleware.php:
--------------------------------------------------------------------------------
1 | enabled) {
45 | $timerStart = $this->debugService->startRequestTimer();
46 | $response = $handler->handle($request->withAttribute(self::TIMING_ATTRIBUTE, $timerStart));
47 | } else {
48 | $response = $handler->handle($request);
49 | }
50 |
51 | return $response;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Classes/Http/Middleware/AddServerTimingMiddleware.php:
--------------------------------------------------------------------------------
1 | handle($request);
46 |
47 | if (! $this->enabled) {
48 | return $response;
49 | }
50 |
51 | $serverTiming = '';
52 | $this->debugService->setStartRequestAt($request->getAttribute(MeasureServerTimingMiddleware::TIMING_ATTRIBUTE));
53 | $metrics = $this->debugService->getMetrics();
54 | foreach ($metrics as $key => ['value' => $value, 'description' => $description]) {
55 | $serverTiming .= ($serverTiming ? ', ' : '') . $key;
56 | if ($description) {
57 | $serverTiming .= ';desc="' . $description . '"';
58 | }
59 | if ($value !== null) {
60 | $serverTiming .= ';dur=' . $value;
61 | }
62 | }
63 |
64 | return $response->withAddedHeader('Server-Timing', $serverTiming);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Classes/Service/RenderTimer.php:
--------------------------------------------------------------------------------
1 | entityManager->getConfiguration()->getSQLLogger();
44 | $queryCount = $sqlLoggingStack instanceof DebugStack ? $sqlLoggingStack->queryCount : 0;
45 |
46 | $this->renderMetrics[$fusionPath] = [
47 | 'startRenderAt' => $this->ts(),
48 | 'sqlQueryCount' => $queryCount,
49 | ];
50 | }
51 |
52 | /**
53 | * Return current microtime in ms
54 | */
55 | private function ts(): float
56 | {
57 | return microtime(true) * 1000;
58 | }
59 |
60 | /**
61 | * Stops the timer and returns the computed values
62 | *
63 | * @return mixed[]|null
64 | */
65 | public function stop(string $fusionPath): ?array
66 | {
67 | if (! array_key_exists($fusionPath, $this->renderMetrics)) {
68 | return null;
69 | }
70 |
71 | $metrics = $this->renderMetrics[$fusionPath];
72 | $sqlLoggingStack = $this->entityManager->getConfiguration()->getSQLLogger();
73 | $queryCount = $sqlLoggingStack instanceof DebugStack ? $sqlLoggingStack->queryCount : 0;
74 |
75 | return [
76 | 'renderTime' => round($this->ts() - $metrics['startRenderAt'], 2) . ' ms',
77 | 'sqlQueryCount' => $queryCount - $metrics['sqlQueryCount'],
78 | ];
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Classes/Logging/DebugStack.php:
--------------------------------------------------------------------------------
1 | parseTableName($sql);
67 | $this->queries[++$this->queryCount] = ['sql' => $sql, 'table' => $tableName, 'params' => $params, 'types' => $types, 'executionMS' => 0];
68 | $this->startTime = microtime(true);
69 | }
70 |
71 | public function stopQuery(): void
72 | {
73 | $executionTime = (microtime(true) - $this->startTime) * 1000;
74 | $this->queries[$this->queryCount]['executionMS'] = $executionTime;
75 | $this->executionTime += $executionTime;
76 |
77 | if ($executionTime > $this->slowQueryAfter) {
78 | $this->slowQueries[] = $this->queries[$this->queryCount];
79 | }
80 |
81 | $table = $this->queries[$this->queryCount]['table'];
82 | if (! array_key_exists($table, $this->tables)) {
83 | $this->tables[$table] = [
84 | 'queryCount' => 1,
85 | 'executionTime' => $executionTime,
86 | ];
87 | } else {
88 | $this->tables[$table]['queryCount']++;
89 | $this->tables[$table]['executionTime'] += $executionTime;
90 | }
91 | }
92 |
93 | protected function parseTableName(string $sql): string
94 | {
95 | $sql = strtolower($sql);
96 | $start = strpos($sql, 'from ') + 5;
97 | $end = strpos($sql, ' ', $start);
98 | $tableName = substr($sql, $start, $end - $start);
99 |
100 | if ($tableName === false) {
101 | return '';
102 | }
103 |
104 | return $tableName;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/Classes/Service/DebugService.php:
--------------------------------------------------------------------------------
1 | startRequestAt = microtime(true) * 1000;
45 | }
46 |
47 | /**
48 | * Sets the starttime of the request
49 | */
50 | public function setStartRequestAt(float $startRequestAt): void
51 | {
52 | $this->startRequestAt = $startRequestAt;
53 | }
54 |
55 | /**
56 | * Stops the timer for the request process
57 | */
58 | public function stopRequestTime(): float
59 | {
60 | return $this->stopRequestAt = microtime(true) * 1000;
61 | }
62 |
63 | /**
64 | * Adds a metric which will be later appended to the http header
65 | *
66 | * @param string $name the short identifier for the metric
67 | * @param float|null $value a numeric float value with up to 2 decimals
68 | * @param string|null $description the short description for the metric
69 | */
70 | public function addMetric(string $name, ?float $value = null, ?string $description = null): void
71 | {
72 | $this->metrics[$this->cleanString($name)] = [
73 | 'value' => $value,
74 | 'description' => $this->cleanString($description),
75 | ];
76 | }
77 |
78 | /**
79 | * Remove any special characters that might break the header
80 | */
81 | protected function cleanString(string $input): string
82 | {
83 | return preg_replace('/[^A-Za-z0-9 ]/', '', $input);
84 | }
85 |
86 | /**
87 | * Returns the time elapsed since `startRequestTime` and will stop the timer
88 | * if it has not been stopped yet.
89 | */
90 | public function getRequestTime(): float
91 | {
92 | if (! $this->stopRequestAt) {
93 | $this->stopRequestTime();
94 | }
95 | return round($this->stopRequestAt - $this->startRequestAt, 2);
96 | }
97 |
98 | /**
99 | * Returns the list of stored metrics including the request time
100 | *
101 | * @return mixed[]
102 | */
103 | public function getMetrics(): array
104 | {
105 | if (! array_key_exists('processRequest', $this->metrics)) {
106 | $this->addMetric('processRequest', $this->getRequestTime(), 'Process request');
107 | }
108 | return $this->metrics;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > [!WARNING]
2 | > **This plugin is no longer being maintained and is superseded by the plugin** https://github.com/Flowpack/neos-debug
3 |
4 | # t3n.Neos.Debug
5 |
6 | The t3n.Neos.Debug package is a small helper package to add a debug panel to your Neos CMS website.
7 | At this point in time you're able to debug your content cache configuration as well as sql queries.
8 | Additionally, the Server-Timing http header can be enabled that will add request timings to responses.
9 | Those then can be viewed in the browser network tab.
10 |
11 | 
12 | 
13 |
14 | ## Installation & configuration
15 |
16 | Install the package via composer
17 |
18 | ```
19 | composer require t3n/neos-debug --dev
20 | ```
21 |
22 | The debug mode is disabled by default. To enable it add this to your Settings.yaml
23 |
24 | ```yaml
25 | t3n:
26 | Neos:
27 | Debug:
28 | enabled: true
29 | ```
30 |
31 | To bring up the debug panel run this command in your js console:
32 | ```js
33 | __enable_neos_debug__()
34 | ```
35 |
36 | _Disclaimer: Once the debug mode is enabled you might expose sensitive data. Make sure to **not** use this in production. At least be warned_
37 |
38 | In a previous version of this package your current user needed a specific role as well. We dropped this requirement for now as you could not use this package if you don't have a frontend login on your site. Once the package is active it will render some metadata in your html output.
39 |
40 | To get the debugger running you now need to include some javascript and css to acutally render the debug console. This package ships two fusion prototypes to include all resources. If your Document extends `Neos.Neos:Page` you don't need to include anything. We already added the resources to `Neos.Neos:Page` prototype.
41 |
42 | ### HTTP Server-Timing header
43 |
44 | The header is disabled by default. To enable it add this to your Settings.yaml
45 |
46 | ```yaml
47 | t3n:
48 | Neos:
49 | Debug:
50 | serverTimingHeader:
51 | enabled: true
52 | ```
53 |
54 | If you only want the header with all timings but not the debug mode, do this:
55 |
56 | ```yaml
57 | t3n:
58 | Neos:
59 | Debug:
60 | enabled: true
61 | htmlOutput:
62 | enabled: false
63 | serverTimingHeader:
64 | enabled: true
65 | ```
66 |
67 | ## Usage
68 |
69 | To enable the cache visualization open your browsers developer console and execute
70 | `__enable_neos_debug__()`. This will bring up the debug console at the bottom of your screen.
71 |
72 | ### 🔦 Inspect
73 |
74 | Once you enable the inspect mode a visualization will pop up and add overlays on your cached parts. Cached parts are marked green, uncached red and dynamic caches are marked yellow. If you hover the loupe you will also see some meta data regarding the cache.
75 |
76 | ### ⚡️ Cache
77 |
78 | This module will add a new modal including some statistics regarding cache hits and misses as well as a table of all rendered cache entries.
79 |
80 | ### 🗄 SQL
81 |
82 | In addition to the content cache we're also exposing some debug SQL informations and statistics. It will also detect slow queries. You can configure from when a query should be marked as slow:
83 |
84 | ```yaml
85 | t3n:
86 | Neos:
87 | Debug:
88 | sql:
89 | # Set when a query should be considered as slow query. In ms
90 | slowQueryAfter: 10
91 | ```
92 |
93 | ### 🚫 Close
94 |
95 | To shutdown the debug console simply close it. If you'd like to persist the active debug state you can add a `true` to the method
96 |
97 | ```
98 | __enable_neos_debug__(true)
99 | ```
100 |
101 | This will set a cookie and the debug mode will still be active after a page refresh.
102 |
103 | > [!WARNING]
104 | > **This plugin is no longer being maintained and is superseded by the plugin** https://github.com/Flowpack/neos-debug
105 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | aliases:
4 | - &ci-build-image quay.io/yeebase/ci-build:7.3
5 | - &workspace_root ~/workspace
6 |
7 | - &save_composer_cache
8 | key: composer-cache-v1-{{ .Branch }}-{{ checksum "composer.json" }}
9 | paths:
10 | - /composer/cache-dir
11 |
12 | - &restore_composer_cache
13 | keys:
14 | - composer-cache-v1-{{ .Branch }}-{{ checksum "composer.json.ci" }}
15 | - composer-cache-v1-{{ .Branch }}-
16 | - composer-cache-v1-
17 |
18 | - &save_js_packages
19 | key: js-packages-v2-{{ .Branch }}-{{ checksum "neos-debug/e2e/package-lock.json" }}
20 | paths:
21 | - /root/.npm
22 | - /root/.cache/Cypress
23 |
24 | - &restore_js_packages
25 | keys:
26 | - js-packages-v2-{{ .Branch }}-{{ checksum "e2e/package-lock.json" }}
27 | - js-packages-v2-{{ .Branch }}-
28 | - js-packages-v2-
29 |
30 | - &attach_workspace
31 | at: *workspace_root
32 |
33 | - &persist_to_workspace
34 | root: .
35 | paths:
36 | - .
37 |
38 | jobs:
39 | checkout:
40 | docker:
41 | - image: *ci-build-image
42 | environment:
43 | COMPOSER_CACHE_DIR: /composer/cache-dir
44 | steps:
45 | - checkout
46 | - restore_cache: *restore_composer_cache
47 | - restore_cache: *restore_js_packages
48 |
49 | - run: |
50 | mkdir neos-debug
51 | shopt -s extglob dotglob
52 | mv !(neos-debug) neos-debug
53 | shopt -u dotglob
54 | cp neos-debug/composer.json.ci composer.json
55 | cp neos-debug/phpcs.xml.dist phpcs.xml.dist
56 | composer update
57 | mkdir -p Configuration
58 | cp neos-debug/e2e/Settings.yaml Configuration/Settings.yaml
59 |
60 | - run: cd neos-debug/e2e && npm ci
61 |
62 | - save_cache: *save_composer_cache
63 | - save_cache: *save_js_packages
64 | - persist_to_workspace: *persist_to_workspace
65 |
66 | lint:
67 | working_directory: *workspace_root
68 | docker:
69 | - image: *ci-build-image
70 | steps:
71 | - attach_workspace: *attach_workspace
72 | - run: bin/phpcs neos-debug/Classes
73 |
74 | e2e:
75 | working_directory: *workspace_root
76 | environment:
77 | FLOW_CONTEXT: Production
78 | docker:
79 | - image: *ci-build-image
80 | - image: circleci/mariadb:10.2
81 | environment:
82 | MYSQL_DATABASE: neos-debug
83 | MYSQL_ROOT_PASSWORD: some-password
84 | steps:
85 | - attach_workspace: *attach_workspace
86 | - restore_cache:
87 | keys:
88 | - js-packages-v2-{{ .Branch }}-{{ checksum "neos-debug/e2e/package-lock.json" }}
89 | - js-packages-v2-{{ .Branch }}-
90 | - js-packages-v2-
91 |
92 | - run: |
93 | ./flow flow:cache:flush
94 | ./flow flow:cache:warmup
95 | ./flow doctrine:migrate
96 | ./flow resource:publish
97 | ./flow site:import --package-key Neos.Demo
98 | - run:
99 | name: Start flow server
100 | command: ./flow server:run --port 8081
101 | background: true
102 | - run: cd neos-debug/e2e && npm run cy
103 |
104 | - store_artifacts:
105 | path: neos-debug/e2e/cypress/videos
106 | - store_artifacts:
107 | path: neos-debug/e2e/cypress/screenshots
108 | - store_artifacts:
109 | path: Data/Logs
110 |
111 | workflows:
112 | version: 2
113 | build_and_test:
114 | jobs:
115 | - checkout:
116 | filters:
117 | branches:
118 | ignore: /dependabot.*/
119 | - lint:
120 | requires:
121 | - checkout
122 | - e2e:
123 | requires:
124 | - checkout
125 |
126 | build_and_test_dependabot:
127 | jobs:
128 | - hold:
129 | type: approval
130 | filters:
131 | branches:
132 | only: /dependabot.*/
133 | - checkout:
134 | requires:
135 | - hold
136 | - lint:
137 | requires:
138 | - checkout
139 | - e2e:
140 | requires:
141 | - checkout
142 |
--------------------------------------------------------------------------------
/Classes/Aspect/CollectDebugInformationAspect.php:
--------------------------------------------------------------------------------
1 | render()) && t3n\Neos\Debug\Aspect\CollectDebugInformationAspect->debuggingActive")
83 | * @Flow\Around("method(Neos\Fusion\View\FusionView->render()) && t3n\Neos\Debug\Aspect\CollectDebugInformationAspect->debuggingActive")
84 | *
85 | * @param \Neos\Flow\AOP\JoinPointInterface $joinPoint
86 | *
87 | * @return string|Response
88 | */
89 | public function addDebugValues(JoinPointInterface $joinPoint)
90 | {
91 | $startRenderAt = microtime(true) * 1000;
92 | $response = $joinPoint->getAdviceChain()->proceed($joinPoint);
93 | $endRenderAt = microtime(true) * 1000;
94 |
95 | $renderTime = round($endRenderAt - $startRenderAt, 2);
96 | $sqlExecutionTime = round($this->sqlLoggingStack->executionTime, 2);
97 |
98 | if ($this->serverTimingHeaderEnabled) {
99 | $this->debugService->addMetric('fusionRenderTime', $renderTime, 'Fusion rendering');
100 | $this->debugService->addMetric('sqlExecutionTime', $sqlExecutionTime, 'Combined SQL execution');
101 | if ($this->contentCacheMisses === 0) {
102 | $this->debugService->addMetric('contentCacheHit', null, 'Content cache hit');
103 | } else {
104 | $this->debugService->addMetric('contentCacheMiss', null, 'Content cache miss');
105 | }
106 | }
107 |
108 | if (! $this->htmlOutputEnabled) {
109 | return $response;
110 | }
111 |
112 | if ($response instanceof Response) {
113 | $output = $response->getBody()->getContents();
114 | $response->getBody()->rewind();
115 |
116 | if ($response->getHeader('Content-Type') !== 'text/html'
117 | && strpos($output, '') === false) {
118 | return $response;
119 | }
120 | } else {
121 | $output = $response;
122 | }
123 |
124 | $data = [
125 | 'startRenderAt' => $startRenderAt,
126 | 'endRenderAt' => $endRenderAt,
127 | 'renderTime' => $renderTime,
128 | 'sqlData' => [
129 | 'queryCount' => $this->sqlLoggingStack->queryCount,
130 | 'executionTime' => $sqlExecutionTime,
131 | 'tables' => $this->sqlLoggingStack->tables,
132 | 'slowQueries' => $this->sqlLoggingStack->slowQueries,
133 | ],
134 | 'cCacheHits' => $this->contentCacheHits,
135 | 'cCacheMisses' => $this->contentCacheMisses,
136 | ];
137 |
138 | $debugOutput = '';
139 | $htmlEndPosition = strpos($output, '