diff --git a/.gitignore b/.gitignore index ff048a9..679092e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/** chrome-webstore-release.zip +firefox-webstore-release.zip diff --git a/extension/html/popup.html b/extension/html/popup.html index de8ac09..d65f761 100644 --- a/extension/html/popup.html +++ b/extension/html/popup.html @@ -17,7 +17,8 @@ width: 350px; outline: none; font-size: 100%; - margin-bottom: 1rem; + padding: 0; + margin: 0.5em 0.5em 1rem; } h1 { @@ -26,20 +27,19 @@ textarea { width: 100%; - height: 100%; + height: 70%; font-size: inherit; } - footer { + p { font-size: 85%; - margin: 0.5rem 0; }

Paths treated as Prometheus endpoints

- +

Separate paths by new line. Regular Expressions are supported, e.g.: ^/prometheus

diff --git a/extension/js/background.js b/extension/js/background.js index 5ff2735..d8512e8 100755 --- a/extension/js/background.js +++ b/extension/js/background.js @@ -8,6 +8,35 @@ const defaultPaths = [ '^/actuator/prometheus' ] +const formatPrometheusMetrics = (body) => body + .split(/\r?\n/) + .map(line => { + // line is a comment + if (/^#/.test(line)) { + return `${line}` + } + + // line is a metric + // Named RegExp groups not supported by Firefox: + // https://bugzilla.mozilla.org/show_bug.cgi?id=1362154 + // const tmp = line.match(/^(?[\w_]+)(?:\{(?.*)\})?\x20(?.+)/) + const tmp = line.match(/^([\w_]+)(?:\{(.*)\})?\x20(.+)/) + + if (tmp && tmp.length > 1) { + let [ _, metric, tags, value ] = tmp + if (tags) { + tags = tags.replace(/([^,]+?)="(.*?)"/g, '$1="$2"') + tags = `{${tags}}` + } + + return `${metric}${tags || ''} ${value}` + } + + // line is something else, do nothing + return line + }) + .join('
') + // Listen for requests from content pages wanting to set up a port chrome.runtime.onConnect.addListener(port => { if (port.name !== 'promformat') { @@ -16,43 +45,14 @@ chrome.runtime.onConnect.addListener(port => { } port.onMessage.addListener(msg => { - if (msg.name !== 'SENDING TEXT') { + if (msg.name !== 'PROMETHEUS_METRICS_RAW_BODY') { return } - const html = msg.payload - .split(/\r?\n/) - .map(line => { - // line is a comment - if (/^#/.test(line)) { - return `${line}` - } - - // line is a metric - // Named RegExp groups not supported by Firefox: - // https://bugzilla.mozilla.org/show_bug.cgi?id=1362154 - // const tmp = line.match(/^(?[\w_]+)(?:\{(?.*)\})?\x20(?.+)/) - const tmp = line.match(/^([\w_]+)(?:\{(.*)\})?\x20(.+)/) - - if (tmp && tmp.length > 1) { - let [ _, metric, tags, value ] = tmp - if (tags) { - tags = tags.replace(/([^,]+?)="(.*?)"/g, '$1="$2"') - tags = `{${tags}}` - } - - return `${metric}${tags || ''} ${value}` - } - - // line is something else, do nothing - return line - }) - .join('
') - - // Post the HTML string to the content script + // Post the HTML string back to the content script port.postMessage({ - name: 'FORMATTED', - payload: html + name: 'PROMETHEUS_METRICS_FORMATTED_BODY', + payload: formatPrometheusMetrics(msg.payload) }) // Disconnect diff --git a/extension/js/content.js b/extension/js/content.js index 62d88cf..b037aaf 100755 --- a/extension/js/content.js +++ b/extension/js/content.js @@ -1,94 +1,91 @@ /* global chrome */ -const compress = (text) => text.replace(/\s+/g, '') +// Don't process HTTP response bodies over 30MB +const MAX_BODY_SIZE_BYTES = 30 * 1024 * 1024 -const maxBodyLength = 3000000 // 3MB - -const style = compress(` - pre { - display:none - } - #promformat { - font-family: monospace; - word-wrap: break-word; - white-space: pre-wrap; - } - .comment { - color: #6a737d; - display: inline-block; - } - br + .comment { - padding-top: 1em; - } - .comment + br + .comment { - padding-top: 0; - } - - .metric { color: #000 } - .value { color: #ff20ed } - .label-key { color: blue } - .label-value { color: green } - `) - -const port = chrome.runtime.connect({ name: 'promformat' }) - -// Add listener to receive response from BG when ready -port.onMessage.addListener(msg => { - switch (msg.name) { - case 'FORMATTED' : - // Insert CSS - const promformatStyle = document.createElement('style') - document.head.appendChild(promformatStyle) - promformatStyle.insertAdjacentHTML('beforeend', style) - - // Insert HTML content - const promformatContent = document.createElement('div') - promformatContent.id = 'promformat' - document.body.appendChild(promformatContent) - - promformatContent.innerHTML = msg.payload - break - - default : - throw new Error(`Message not understood: ${msg.name}`) - } -}) - -function ready (data) { +const sendBodyToFormatter = (storedData) => { // Check if it is a Prometheus plain text response // This is quite a basic assumption, as the browser cannot access the // 'version' part of the content type to verify. - const paths = data.paths.length ? data.paths : [] - if (document.contentType !== 'text/plain') { + port.disconnect() return } - for (var i = 0; i < paths.length; ++i) { - if (document.location.pathname.match(paths[i])) { - format() - break - } + // Check if the current page's paths matches one of our whitelist + if (!storedData.paths.some(path => document.location.pathname.match(path))) { + port.disconnect() + return } -} -function format () { - // Check if plain text wrapped in
 element exists and doesn't exceed maxBodyLength
+  // Check if plain text wrapped in 
 element exists and doesn't exceed
+  // MAX_BODY_SIZE_BYTES
   const pre = document.body.querySelector('pre')
   const rawBody = pre && pre.innerText
 
-  if (!rawBody || rawBody.length > maxBodyLength) {
+  if (!rawBody || rawBody.length > MAX_BODY_SIZE_BYTES) {
     port.disconnect()
     return
   }
 
   // Post the contents of the PRE
   port.postMessage({
-    name: 'SENDING TEXT',
+    name: 'PROMETHEUS_METRICS_RAW_BODY',
     payload: rawBody
   })
 }
 
+const renderFormattedHTML = (html) => {
+  const style = `
+    pre {
+      display:none
+    }
+    #promformat {
+      font-family: monospace;
+      word-wrap: break-word;
+      white-space: pre-wrap;
+    }
+    .comment {
+      color: #6a737d;
+      display: inline-block;
+    }
+    br + .comment {
+      padding-top: 1em;
+    }
+    .comment + br + .comment {
+        padding-top: 0;
+    }
+
+    .metric      { color: #000 }
+    .value       { color: #ff20ed }
+    .label-key   { color: blue }
+    .label-value { color: green }
+    `
+
+  // Insert CSS
+  const promformatStyle = document.createElement('style')
+  document.head.appendChild(promformatStyle)
+  promformatStyle.insertAdjacentHTML('beforeend', style)
+
+  // Insert HTML content
+  const promformatContent = document.createElement('div')
+  promformatContent.id = 'promformat'
+  document.body.appendChild(promformatContent)
+
+  promformatContent.innerHTML = html
+}
+
+const port = chrome.runtime.connect({ name: 'promformat' })
+
+// Add listener to receive response from background when ready
+port.onMessage.addListener(msg => {
+  if (msg.name !== 'PROMETHEUS_METRICS_FORMATTED_BODY') {
+    return
+  }
+
+  renderFormattedHTML(msg.payload)
+})
+
 document.addEventListener('DOMContentLoaded', () => {
-  chrome.storage.sync.get({ paths: [] }, ready)
+  chrome.storage.sync.get({ paths: [] }, sendBodyToFormatter)
 })
diff --git a/extension/manifest.json b/extension/manifest.json
index 5111ad0..3c2ad2d 100755
--- a/extension/manifest.json
+++ b/extension/manifest.json
@@ -1,6 +1,6 @@
 {
   "name": "Prometheus Formatter",
-  "version": "1.2.4",
+  "version": "2.0.0",
   "manifest_version": 2,
   "description": "Makes plain Prometheus metrics easier to read.",
   "homepage_url": "https://github.com/fhemberger/prometheus-formatter",
@@ -37,11 +37,5 @@
   "permissions": [
     "activeTab",
     "storage"
-  ],
-  "browser_specific_settings": {
-    "gecko": {
-      "id": "prometheus-formatter@frederic-hemberger.de",
-      "strict_min_version": "72.0"
-    }
-  }
-}
+  ]
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index ea6e0af..15c0103 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
   "description": "Browser extension which makes plain Prometheus metrics easier to read.",
   "scripts": {
     "test": "standard js/*.js --fix",
-    "release": "node update-manifest.js && cd extension && zip -r ../webstore-release.zip html icons js LICENSE.txt manifest.json -x icons/.DS_Store js/.DS_Store .DS_Store"
+    "release": "./package/package.sh"
   },
   "author": "Frederic Hemberger",
   "license": "MIT",
diff --git a/package/package.sh b/package/package.sh
new file mode 100755
index 0000000..c493375
--- /dev/null
+++ b/package/package.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+set -uo pipefail
+
+readonly DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
+
+function package_zip () {
+  cd "$1" && zip -r "$2" html icons js LICENSE.txt manifest.json -x icons/.DS_Store js/.DS_Store .DS_Store
+}
+
+# Update manifest version number from package.json
+"$DIR/update-manifest-version.js"
+
+# Package Chrome webstore release
+package_zip "$DIR/../extension" "$DIR/../chrome-webstore-release.zip"
+
+# Package Firefox webstore release
+cp -r "$DIR/../extension" "$DIR/../extension-firefox"
+"$DIR/update-manifest-firefox.js"
+package_zip "$DIR/../extension-firefox" "$DIR/../firefox-webstore-release.zip"
+rm -r "$DIR/../extension-firefox"
diff --git a/package/update-manifest-firefox.js b/package/update-manifest-firefox.js
new file mode 100755
index 0000000..4713049
--- /dev/null
+++ b/package/update-manifest-firefox.js
@@ -0,0 +1,16 @@
+#!/usr/bin/env node
+
+const fs = require('fs')
+const path = require('path')
+const manifestPath = path.join(__dirname, '..', 'extension-firefox', 'manifest.json')
+const manifest = require(manifestPath)
+
+// Firefox needs additional keys in the manifest.json which are not allowed in Chrome
+manifest.browser_specific_settings = {
+  gecko: {
+    id: 'prometheus-formatter@frederic-hemberger.de',
+    strict_min_version: '72.0'
+  }
+}
+
+fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf8')
diff --git a/package/update-manifest-version.js b/package/update-manifest-version.js
new file mode 100755
index 0000000..c27ddb8
--- /dev/null
+++ b/package/update-manifest-version.js
@@ -0,0 +1,10 @@
+#!/usr/bin/env node
+
+const fs = require('fs')
+const path = require('path')
+const pkg = require(path.join(__dirname, '..', 'package.json'))
+const manifestPath = path.join(__dirname, '..', 'extension', 'manifest.json')
+const manifest = require(manifestPath)
+
+manifest.version = pkg.version
+fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf8')
diff --git a/update-manifest.js b/update-manifest.js
deleted file mode 100644
index 385f10e..0000000
--- a/update-manifest.js
+++ /dev/null
@@ -1,6 +0,0 @@
-const fs = require('fs')
-const pkg = require('./package.json')
-const manifest = require('./extension/manifest.json')
-
-manifest.version = pkg.version
-fs.writeFileSync('./extension/manifest.json', JSON.stringify(manifest, null, 2), 'utf8')