{"_id":"apostrophe-profiler","_rev":"30-3c6b82c23873548656a54ea25f3f3830","name":"apostrophe-profiler","dist-tags":{"latest":"2.1.1"},"versions":{"2.0.0":{"name":"apostrophe-profiler","version":"2.0.0","keywords":["debug","apostrophe","cms","apostrophe"],"author":{"name":"P'unk Avenue LLC"},"license":"MIT","_id":"apostrophe-profiler@2.0.0","maintainers":[{"name":"boutell","email":"tom@punkave.com"}],"homepage":"https://github.com/punkave/apostrophe-profiler#readme","bugs":{"url":"https://github.com/punkave/apostrophe-profiler/issues"},"dist":{"shasum":"c157413e63ec179818605f96c86fc33781b3e977","tarball":"https://registry.npmjs.org/apostrophe-profiler/-/apostrophe-profiler-2.0.0.tgz","fileCount":7,"integrity":"sha512-CHn+Dvn0G6+wr1eEW1DCofXoho4k+UibQaxpkqbbG1R9/5Ck/1SB/YzhUL0yyw4bGO2gxAOXUDPfB0vPpS2aqw==","signatures":[{"sig":"MEYCIQCLgHA+o3Tz1PsEngujHaCe4FHNaEXW3MgHAT62BkkdgwIhAPuQOwSRM4jWAxDnzjajGFWS67O+zRitHFX/Cp7HAbz0","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":18275,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMQE0CRA9TVsSAnZWagAA8BQP/RBxF6qwhKolHWv4P3nb\not1tokwXlB1T6+GZeJ/lrKPSDP/rBRftynmkjzmLBTZM2nGbnlMlUlSFi+8J\nyLaA0dcnQcYJw7Hm9vKetSA+wLsi8hlXu51wu/Q6TQKxduivqumioPBhtX3o\n2imh0sGrxF/zh/qeT+JwLwxaHEktxKwRDo4TPUQVj2bvMHw8bVF9BrkyGdUU\nZMOeHWte9ilrQD0H39NVeYOIueQ4allS5vZM4FN0YzaA9g8opM2GFU7sVXy7\n126FPj5aw0qMDSR2KbDJOyJ+a/VDlweiBvK4mn5cIhq+l4F39OX7dd+W4uHD\nds4F/9vynfEGepPei1D4SiZZZ0wK7UgFXMWP06k8sZ0veMk/ss2YM+03ER/A\n2vexGERhaP3lnU9PN7FrRwJdmcS67Br1rESM6DoLVglyAYDbKTxLfHcUS+es\ntIJs5kH3YrTAyWZQAHjAXPkGfYcxIKe0eBfxMmjhNNb8gCWG34o8kwDPxSgb\naiZPukDJgmAyylDSacUE4bW/Bx4fLjOwfGqANaDaAxGYwRqP9acLsVlq8bmU\nIXe36/hRiO1y5nkSpIRH7f+cHBhL/yzqt64Un1DoaDz9JI26eGfncBfCUCNt\n/Vu0AB24nuCA25lCSmxLXYbP6mNJWl7PEaxJvua4/oy2Nlo+kKTtbmv8SBUl\nvrpj\r\n=GYy4\r\n-----END PGP SIGNATURE-----\r\n"},"main":"index.js","gitHead":"643e052d5eb4d73e76576d1f1d799a026690e1df","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"boutell","email":"tom@punkave.com"},"repository":{"url":"git+https://github.com/punkave/apostrophe-profiler.git","type":"git"},"_npmVersion":"5.6.0","description":"Performance profiling tool for Apostrophe 2.x+","directories":{},"_nodeVersion":"8.10.0","dependencies":{"async":"^2.6.1","lodash":"^4.17.4","bluebird":"^3.5.1"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/apostrophe-profiler_2.0.0_1529938228099_0.6994721612240231","host":"s3://npm-registry-packages"}},"2.1.0":{"name":"apostrophe-profiler","version":"2.1.0","keywords":["debug","apostrophe","cms","apostrophe"],"author":{"name":"P'unk Avenue LLC"},"license":"MIT","_id":"apostrophe-profiler@2.1.0","maintainers":[{"name":"alexbea","email":"alex.bea@gmail.com"},{"name":"alexgilbert","email":"alex@punkave.com"},{"name":"austinstarin","email":"austin@punkave.com"},{"name":"bgantick","email":"bgantick@gmail.com"},{"name":"bobclewell","email":"bobclewell@gmail.com"},{"name":"boutell","email":"tom@punkave.com"},{"name":"grdunn","email":"grdunn@gmail.com"},{"name":"mtthwmnc","email":"matthew.mance@gmail.com"},{"name":"stuartromanek","email":"stuart@punkave.com"}],"homepage":"https://github.com/punkave/apostrophe-profiler#readme","bugs":{"url":"https://github.com/punkave/apostrophe-profiler/issues"},"dist":{"shasum":"be727495ceb9a6ba8fa5cf1053a606210e5c0504","tarball":"https://registry.npmjs.org/apostrophe-profiler/-/apostrophe-profiler-2.1.0.tgz","fileCount":8,"integrity":"sha512-JY8ZdcR6ZUIvzgtfctc8ZAZA1JB3+sWXPNW+MuQDpmLZDsO7efXNT1SluMTrXNjNLhHlbX72xYEuhqHUdAHr/g==","signatures":[{"sig":"MEYCIQCD+pq8DOszXZTiEC4Tw28NXwgKbXD9lYkloFVdHPJX+wIhAMpQ/WikxJgs+Eigb98JyIyDWlZLd1a8+qhAhsaKamcc","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":27423,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJboVUNCRA9TVsSAnZWagAAF7MP/2Ud4KJLdusMuZKRJlJY\n3jPBiY+IXF2Vt0G70W6ULoRu6idwJRIWuGkwIeoYnD3uUYlz+s9Cbgya7pc6\n3XaEk6Wok1gI954u6ZruEnHFIn1Tv6Cyyvyjgd09M9PsGADeIgsFZAhA0xkT\nsbKzZh2ShfnZJPz4+5taDJnNfgbnZlZnDUYifz9bFnceCHxp6dkXTHPigUtw\nZ/SklOJ0tjbBTn8xlWI4As1Hk+Y/Do57RVIVVfqUOR93nWxHDz2t+AG1VO5t\nfC8g68DP6dsm3+8BoJWrbUK4Ht2xZzONlUVz89kXDxuIyWOT69S4Y0Z8NwM0\n5Hu/U/n+wumYc1FlJR6T2nbb6KU8n9u6Gqtns5KZWurld1smzjHX0xPtOHmG\n7kApsesksoi/8hN7qzyFAGnlFVjOrib6XkBieQLL1FApl/4PS4HixpXC+bcC\nKeb75idBRIn5Zw5bdTidBOFiLNsU601cj+BO91CW0ZkRI2qctYji2zsYzvpc\nb664OGaxZ9AFnFPDBOH9mtXcO9AAOdtQnF3lPsuItV7mrkMknYxJ77/sbkUG\nn1K3raQ4DjfoAkygtIXggRctDvTiUgYbYhhyu5L/jQjp3pzx1K45Y0lJQ8kw\nPB0rZ0zUtPnFeMUxkMs9Hi02a1lX3qfrWeKR7trIdnNEqsp+VXrVfGiFwYY0\nzTMI\r\n=OV/w\r\n-----END PGP SIGNATURE-----\r\n"},"main":"index.js","gitHead":"193b857f15f9ec32bde556ce57f50b700c1a45c8","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"boutell","email":"tom@punkave.com"},"repository":{"url":"git+https://github.com/punkave/apostrophe-profiler.git","type":"git"},"_npmVersion":"6.3.0","description":"Performance profiling tool for Apostrophe 2.x+","directories":{},"_nodeVersion":"8.10.0","dependencies":{"async":"^2.6.1","lodash":"^4.17.4","bluebird":"^3.5.1"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/apostrophe-profiler_2.1.0_1537299725021_0.3007475646765405","host":"s3://npm-registry-packages"}},"2.1.1":{"name":"apostrophe-profiler","version":"2.1.1","keywords":["debug","cms","apostrophe","apostrophecms","apostrophe-cms","content management","content management system"],"author":{"name":"P'unk Avenue LLC"},"license":"MIT","_id":"apostrophe-profiler@2.1.1","maintainers":[{"name":"alexbea","email":"alex.bea@gmail.com"},{"name":"alexgilbert","email":"alex@punkave.com"},{"name":"austinstarin","email":"austin@punkave.com"},{"name":"bgantick","email":"bgantick@gmail.com"},{"name":"bobclewell","email":"bobclewell@gmail.com"},{"name":"boutell","email":"tom@punkave.com"},{"name":"grdunn","email":"grdunn@gmail.com"},{"name":"mtthwmnc","email":"matthew.mance@gmail.com"},{"name":"stuartromanek","email":"stuart@punkave.com"}],"homepage":"https://github.com/punkave/apostrophe-profiler#readme","bugs":{"url":"https://github.com/punkave/apostrophe-profiler/issues"},"dist":{"shasum":"2ee892a667d41210cf5aa5d9fe642522cb0d3bd5","tarball":"https://registry.npmjs.org/apostrophe-profiler/-/apostrophe-profiler-2.1.1.tgz","fileCount":8,"integrity":"sha512-RjpJ0pGa7XzCs4+b+7M9CTw/G9Nw91SdsaoDD8+GN5b44Q4q8B1TR1vNg/9Zjq0nnA52Y+Zo/2DLlRKQHkQESQ==","signatures":[{"sig":"MEUCIFysnYPh4wxyxsbNRKYkChXC0cP8dJvMR/WJDdR7uOzKAiEAgXLG1sBo1erCMqdMvylx5bqosbYu825H0jFS2wS6u0A=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":27742,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbo804CRA9TVsSAnZWagAAjigP/i1I4WGMDD4AaERQreV0\nQv1hEtL39k6cCrHJ2zQFMiu7PZRXPXSGig4eFHWzx9DFMZ+hb+tz5qfo22aF\ngxQHNUJ/vdz1EjPG7+QP4XsKg3H5pvU+Zew44qojImxGAbiU+OEe4M8Jyr7J\nDc0xvi6Gtiy4NRgEwUJPWapgwUz8AjUocgKLsFlUcbjvsDl0ZEewbjezYndv\nvpf9faLJWsIbcIoFL0K9zMr+Sifje5Hqd8NyKhSjfF26IXDzUbtQtCSJcVtt\nWlqSF1fD6jt/efCjKGPJqM98qPo9xKNWczyaIjiBdmOsKFoPB0YJHAhnCtDV\nKRHsJZsMTVg/oZ7gljK0naayTSngCecNCX6LXPRJGpGeWBj3AQPgH3L4Calm\nvgiSPfG+d+U9AlB5WMfLHjLYYP+GtJ7VeO1pR/JJkJwSLIldwYarJNRhe9Ol\ndGwVTCEht0XcQNuIYA2ZYt0bxBkcL24lnraiNrA6EOgub+SYSqEFN6MsniZl\niK7aEzg6izr4/LA2pASqXhv3CsCYbj5OcoT5Da4IzPE78WB5oLrrMKeNede9\nbXSdsxhcuJBFGvRI9NTR7OCjjF0mmjrJcAgoOMGbSAwQ6Cebk1AqxDRejP39\njrHia9G/EuQfDpyKsKuHN7OiSOPfGuh0tDpk0CWqL1/V/azsWDj9xjqduKT+\nuutP\r\n=E3q+\r\n-----END PGP SIGNATURE-----\r\n"},"main":"index.js","gitHead":"3adeba9a90149adca0b98ca856b277b494ad1f1d","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"boutell","email":"tom@punkave.com"},"repository":{"url":"git+https://github.com/punkave/apostrophe-profiler.git","type":"git"},"_npmVersion":"6.3.0","description":"Performance profiling tool for Apostrophe 2.x+","directories":{},"_nodeVersion":"8.10.0","dependencies":{"async":"^2.6.1","lodash":"^4.17.4","bluebird":"^3.5.1"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/apostrophe-profiler_2.1.1_1537461559119_0.25265809317682786","host":"s3://npm-registry-packages"}}},"time":{"created":"2018-06-25T14:50:28.099Z","modified":"2026-01-26T19:10:38.913Z","2.0.0":"2018-06-25T14:50:28.140Z","2.1.0":"2018-09-18T19:42:05.232Z","2.1.1":"2018-09-20T16:39:19.255Z"},"bugs":{"url":"https://github.com/punkave/apostrophe-profiler/issues"},"author":{"name":"P'unk Avenue LLC"},"license":"MIT","homepage":"https://github.com/punkave/apostrophe-profiler#readme","keywords":["debug","cms","apostrophe","apostrophecms","apostrophe-cms","content management","content management system"],"repository":{"url":"git+https://github.com/punkave/apostrophe-profiler.git","type":"git"},"description":"Performance profiling tool for Apostrophe 2.x+","maintainers":[{"email":"alex@apostrophecms.com","name":"alexgilbert"},{"email":"tom@apostrophecms.com","name":"boutell"},{"email":"stuart+npm@apostrophecms.com","name":"romanek"},{"email":"robert.means1969+apostrophecms@gmail.com","name":"bodonkey"},{"email":"greg.vanbrug@gmail.com","name":"gregvanbrug"},{"email":"stuart@punkave.com","name":"stuartromanek"},{"email":"abea@mailbox.org","name":"alexbea"},{"email":"matthew.mance@gmail.com","name":"mtthwmnc"},{"email":"breyell@gmail.com","name":"breyell"},{"email":"vjeudy@protonmail.com","name":"valjed"}],"readme":"# apostrophe-profiler\n\nThis module finds slow code. It provides performance profiling for the [Apostrophe CMS](https://apostrophecms.org), version 2.x or better.\n\nThis module only examines performance on the server side (it helps you optimize Time to First Byte, or TTFB). For browser-side performance issues, make sure you have enabled `minify: true` for the `apostrophe-assets` module and use tools like Pagespeed to get more suggestions.\n\n## Installation\n\nEdit your `package.json` file. Add `apostrophe-profiler` to your dependencies:\n\n```\n'apostrophe-profiler': 'apostrophecms/apostrophe-profiler'\n```\n\nRun `npm install`.\n\nNow turn it on in your project:\n\n```javascript\n// in data/local.js, not app.js\n\nmodule.exports = {\n  modules: {\n    apostrophe-profiler: {}\n  }\n};\n```\n\n> Anything in your `data/local.js` file merges with the configuration object in `app.js`, and our standard deployment recipes *do not* deploy this file. That's why it's the logical place to turn on debugging features in a dev environment.\n\n## Finding slow code\n\n**By default, nothing happens.** Turn it on with an environment variable\nwhen you test your app:\n\n```\nAPOS_PROFILER=per-request node app\n```\n\nAfter each request, you'll see output like this:\n\n```\nRequest: /\n\nTotal time: 294.52ms (100.00%)\n\nItems taking over 1% of time\n\ncursor: 256.82ms (87.20%)\ncursor.apostrophe-pages-cursor: 137.28ms (46.61%)\ncursor.apostrophe-pages-cursor.after: 109.96ms (37.34%)\ncursor.apostrophe-global-cursor: 59.67ms (20.26%)\ncursor.apostrophe-global-cursor.after: 56.59ms (19.21%)\nserveGetPage: 46.35ms (15.74%)\ncursor.apostrophe-pages-cursor.main: 27.32ms (9.27%)\nwidget: 24.16ms (8.20%)\ncursor.apostrophe-images-cursor: 23.89ms (8.11%)\ncursor.apostrophe-blog-cursor: 20.60ms (6.99%)\ncallAll: 18.91ms (6.42%)\ncallAll.pageBeforeSend: 18.09ms (6.14%)\ncursor.apostrophe-images-cursor.main: 17.46ms (5.93%)\nwidget.apostrophe-images: 17.27ms (5.86%)\ncallAll.pageBeforeSend.apostrophe-pages: 16.53ms (5.61%)\ncursor.apostrophe-blog-cursor.after: 14.08ms (4.78%)\nwidget.apostrophe-images.load: 12.04ms (4.09%)\ncursor.apostrophe-events-cursor: 8.49ms (2.88%)\ncursor.apostrophe-blog-cursor.main: 6.52ms (2.22%)\ncursor.apostrophe-images-cursor.after: 6.43ms (2.18%)\ncursor.apostrophe-events-cursor.main: 5.51ms (1.87%)\nwidget.apostrophe-images.output: 5.22ms (1.77%)\nwidget.apostrophe-files.load: 4.44ms (1.51%)\nwidget.apostrophe-files: 4.44ms (1.51%)\ncursor.apostrophe-files-cursor: 3.27ms (1.11%)\ncursor.apostrophe-global-cursor.main: 3.08ms (1.05%)\ncursor.apostrophe-events-cursor.after: 2.98ms (1.01%)\n```\n\nIf you prefer, you can get cumulative figures every 10 seconds. This is\nuseful when testing heavy loads, and also includes figures for\nactivity that did not take place inside a web request:\n\n```\nAPOS_PROFILER=cumulative node app\n```\n\n> The cumulative view is usually best for simulating production loads as it smooths out differences between individual requests.\n\n## Understanding the output\n\nThe output for time spent in widgets is worth discussing. Each widget's time is broken down into `load` time and `output` time. The `load` time is spent in `load` methods and may involve third-party APIs or just Apostrophe joins. The `output` time is spent in your Nunjucks templates and helper functions.\n\nThe output for time spent in cursors is interesting too. Time spent in `main` is usually attributable to the actual MongoDB query for that type of document. Time spent in `after` is usually attributable to joins, widget loaders for content in the document, and so on.\n\n## Changing the threshold of \"interesting\" activity\n\nBy default, activities that consumed less than 1% of the time\nare not shown. You can change this figure, for instance\nto 2%:\n\n```\nAPOS_PROFILER=cumulative,threshold=2 node app\n```\n\n## \"How come the numbers don't add up to 100% and some exceed 100%?\"\n\nMany categories overlap. Time spent in cursors is also part of the\ntime spent in various `callAll` methods like `pageBeforeSend`.\nThe same goes for widgets and cursors. \n\nAlso, cursors may invoke other cursors, which tracks the time in both places. Look at the `.main` portion to see what was done outside of `.after`, which carries out nested joins. This is why the overall figure for `cursors` can be misleading.\n\n## Tracking time in your own code\n\nThe profiler already breaks down for you which widgets,\ncursor types and `callAll` methods such as `pageBeforeSend`\ntook up the most time. You can also track time yourself,\nlike this:\n\n```javascript\n// Get a function to call after the action is complete\nconst p = self.apos.utils.profile(req, 'external-apis.twitter.list');\n// Let's call a made-up Twitter API with a callback as an example\nreturn twitter.getList(function(err, list) {\n  // Tell the profiler the action is complete\n  p();\n  // Do something with err and list here\n});\n```\n\nIf you prefer you can report the time consumed yourself, in\nmilliseconds:\n\n```javascript\nself.apos.utils.profile(req, 'external-apis.twitter.list', 50);\n```\n\nNotice we pass `req` and a key identifying the action. If\nthere is no web request involved, you can omit `req` entirely\nor pass `null`. The key is namespaced with `.` and the\nmost general category should come first, to add up all of the\ntime tracked in that category.\n\n> It is safe to call this API even when this module is not installed. The `apostrophe` module ships with a stub version that does nothing so you can ship your own modules with profiling calls in place.\n\n## Speeding up the database: `index-suggestions`\n\nThis module can also suggest additional MongoDB database indexes for your database. Apostrophe adds several \"out of the box,\" but your project may have its own common queries that are sped up further by adding your own.\n\nTo activate this feature, use:\n\n```\nAPOS_PROFILER=index-suggestions node app\n```\n\nA report will be printed every 5 seconds, assuming there have been new queries that result in new index suggestions.\n\n### Interpreting index suggestions\n\n*You have to apply your own knowledge when interpreting these suggestions.* For instance, the module might suggest many indexes, but you have reason to believe MongoDB will select one that has enough of the properties in common for several of the cases.\n\nHowever, be aware that MongoDB can make mistakes. For instance, if your query involves an equality check that you know will return only one document, but a sort like `updatedAt` is still explicitly or implicitly present, MongoDB may believe that it should choose the default index on `updatedAt`, which will result in scanning the entire database. So in such cases you should change your actual query to get rid of the sort:\n\n`self.find(req, { slug: 'one-unique-thing' }).sort(false)...`\n\nOr, go ahead and add the suggested indexes that include the sort.\n\nAlso, be aware that with the exception of a few standard indexes in Apostrophe, *this feature does not know what indexes you already have or how they might already be meeting the need.*\n\nFinally, *remember that MongoDB limits you to 64 databases per collection*, and that more indexes mean more time and space consumed when inserting documents. Usually, for a CMS-driven website, this is fine because you are much more interested in read performance. But, do bear it in mind.\n\n> This module only makes index suggestions based on queries that flow normally through Apostrophe's cursor APIs, such as typical page and piece loading queries. Direct MongoDB queries on the collection object are not analyzed.\n\n### Adding an index\n\nYou can do this directly in the MongoDB shell:\n\n```\ndb.aposDocs.ensureIndex({ slug: 1, type: 1, updatedAt: -1 })\n```\n\nYou can also do it programmatically in any module. For instance:\n\n```\n// in lib/modules/my-module/index.js\nmodule.exports = {\n  afterConstruct: function(self, callback) {\n    return self.addCustomIndex(callback);\n  },\n  construct: function(self, options) {\n    self.addCustomIndex = function(callback) {\n      return self.apos.docs.db.ensureIndex(\n        { slug: 1, type: 1, updatedAt: -1 },\n        callback\n      );\n    };\n  }\n};\n```\n\nFor more information see the MongoDB documentation.\n\n### Displaying the indexes you already have\n\nOne more MongoDB shell tip: you can list the indexes you already have, to make sure the one you are adding is not redundant or similar enough to be unnecessary.\n\n```\ndb.aposDocs.getIndexes()\n```\n\n## \"Great, but what else can I do to make my code faster?\"\n\nA few tips to get you started:\n\n* Always set projections with your joins, or `areas: false, joins: false`.\n* Don't hit third-party web APIs on every request. Always cache those results.\n* Don't hit third-party APIs at all when building the page, if you can\navoid it. Instead, hit them via AJAX requests after the page loads.\n* Use the `apostrophe-global` module wisely. Don't fill it with joins,\nespecially to things you don't need on every single request. *Do*\nuse it to directly hold things you would otherwise join with but need\non every request.\n* Use the `index-suggestions` feature of this module, but remember that you must apply your own knowledge as well.\n\n\n","readmeFilename":"README.md"}