From 87155bec7166aec036f1f861d57c17d102261ebb Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Thu, 8 Jul 2021 09:56:20 +0200 Subject: [PATCH] Print diagnose report from CLI (#401) * Add diagnose integration tests * Run diagnose integration tests on CI * Add cli/diagnose with header and library section * Add extension installation report * Add configuration section to diagnose * Add config diagnose link * Add API key validation section * Add the paths section * Add diagnose reports section * Add diagnostics report section * Restore diagnose report sending * Move report sending to separate function * Restore --(no-)send-report options * Update diagnose_tests to remove log paths from Node.js diagnose * Print specific list of configuration values in diagnose * Remove logFilePath from JS_TO_RUBY_MAPPING * Add linux arm override to diagnose * Update diagnose_tests to silence debug output on CI * Update packages/nodejs/src/cli/diagnose.ts Co-authored-by: Tom de Bruijn * Add changeset for diagnose Co-authored-by: Tom de Bruijn --- .gitmodules | 3 + .semaphore/semaphore.yml | 35 ++++ build_matrix.yml | 5 + packages/nodejs/.changesets/diagnose.md | 5 + packages/nodejs/bin/diagnose | 76 +------- packages/nodejs/src/cli/diagnose.ts | 248 ++++++++++++++++++++++++ test/integration/diagnose | 1 + 7 files changed, 300 insertions(+), 73 deletions(-) create mode 100644 .gitmodules create mode 100644 packages/nodejs/.changesets/diagnose.md create mode 100644 packages/nodejs/src/cli/diagnose.ts create mode 160000 test/integration/diagnose diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..86438932 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "test/integration/diagnose"] + path = test/integration/diagnose + url = git@github.com:appsignal/diagnose_tests.git diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index cde1a920..c6c7e69c 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -73,6 +73,11 @@ blocks: - name: "@appsignal/nodejs - nodejs" commands: - mono test --package=@appsignal/nodejs + - name: "@appsignal/nodejs - nodejs - diagnose" + commands: + - git submodule init + - git submodule update + - LANGUAGE=nodejs test/integration/diagnose/bin/test - name: "@appsignal/nodejs-ext - nodejs-ext" commands: - mono test --package=@appsignal/nodejs-ext @@ -125,6 +130,11 @@ blocks: - name: "@appsignal/nodejs - nodejs" commands: - mono test --package=@appsignal/nodejs + - name: "@appsignal/nodejs - nodejs - diagnose" + commands: + - git submodule init + - git submodule update + - LANGUAGE=nodejs test/integration/diagnose/bin/test - name: "@appsignal/nodejs-ext - nodejs-ext" commands: - mono test --package=@appsignal/nodejs-ext @@ -177,6 +187,11 @@ blocks: - name: "@appsignal/nodejs - nodejs" commands: - mono test --package=@appsignal/nodejs + - name: "@appsignal/nodejs - nodejs - diagnose" + commands: + - git submodule init + - git submodule update + - LANGUAGE=nodejs test/integration/diagnose/bin/test - name: "@appsignal/nodejs-ext - nodejs-ext" commands: - mono test --package=@appsignal/nodejs-ext @@ -229,6 +244,11 @@ blocks: - name: "@appsignal/nodejs - nodejs" commands: - mono test --package=@appsignal/nodejs + - name: "@appsignal/nodejs - nodejs - diagnose" + commands: + - git submodule init + - git submodule update + - LANGUAGE=nodejs test/integration/diagnose/bin/test - name: "@appsignal/nodejs-ext - nodejs-ext" commands: - mono test --package=@appsignal/nodejs-ext @@ -281,6 +301,11 @@ blocks: - name: "@appsignal/nodejs - nodejs" commands: - mono test --package=@appsignal/nodejs + - name: "@appsignal/nodejs - nodejs - diagnose" + commands: + - git submodule init + - git submodule update + - LANGUAGE=nodejs test/integration/diagnose/bin/test - name: "@appsignal/nodejs-ext - nodejs-ext" commands: - mono test --package=@appsignal/nodejs-ext @@ -333,6 +358,11 @@ blocks: - name: "@appsignal/nodejs - nodejs" commands: - mono test --package=@appsignal/nodejs + - name: "@appsignal/nodejs - nodejs - diagnose" + commands: + - git submodule init + - git submodule update + - LANGUAGE=nodejs test/integration/diagnose/bin/test - name: "@appsignal/nodejs-ext - nodejs-ext" commands: - mono test --package=@appsignal/nodejs-ext @@ -385,6 +415,11 @@ blocks: - name: "@appsignal/nodejs - nodejs" commands: - mono test --package=@appsignal/nodejs + - name: "@appsignal/nodejs - nodejs - diagnose" + commands: + - git submodule init + - git submodule update + - LANGUAGE=nodejs test/integration/diagnose/bin/test - name: "@appsignal/nodejs-ext - nodejs-ext" commands: - mono test --package=@appsignal/nodejs-ext diff --git a/build_matrix.yml b/build_matrix.yml index 4db6dff5..8a5aa9d6 100644 --- a/build_matrix.yml +++ b/build_matrix.yml @@ -55,6 +55,11 @@ matrix: path: "packages/nodejs" variations: - name: "nodejs" + extra_tests: + diagnose: + - git submodule init + - git submodule update + - LANGUAGE=nodejs test/integration/diagnose/bin/test - package: "@appsignal/nodejs-ext" path: "packages/nodejs-ext" variations: diff --git a/packages/nodejs/.changesets/diagnose.md b/packages/nodejs/.changesets/diagnose.md new file mode 100644 index 00000000..7f74c08f --- /dev/null +++ b/packages/nodejs/.changesets/diagnose.md @@ -0,0 +1,5 @@ +--- +bump: "minor" +--- + +Format and print the diagnose report in a human-readable format. diff --git a/packages/nodejs/bin/diagnose b/packages/nodejs/bin/diagnose index f2b47eec..96b773c3 100755 --- a/packages/nodejs/bin/diagnose +++ b/packages/nodejs/bin/diagnose @@ -2,76 +2,6 @@ "use strict" -const https = require("https") -const path = require("path") -const util = require("util") -const fs = require("fs") -const { DiagnoseTool } = require("../dist/diagnose") - -// enable diagnose mode -process.env["_APPSIGNAL_DIAGNOSE"] = "true" - -// providing --send-report sends the report. --no-send-report stops the report from being sent -// it can also be passed by @appsignal/cli -const shouldSendReport = process.argv.includes("--send-report") && !process.argv.includes("--no-send-report") - -const tool = new DiagnoseTool({}) - -console.log(` -🔧 AppSignal Diagnose Tool - -Use this information to debug your configuration. -More information is available on the documentation site. -https://docs.appsignal.com/ - -This diagnose output ${ - shouldSendReport ? "was" : "was not" -} sent to AppSignal, contact us at support@appsignal.com if you need help. -`) - -if (!process.env["APPSIGNAL_PUSH_API_KEY"]) { - throw new Error( - `No Push API key found. Set the APPSIGNAL_PUSH_API_KEY environment variable to your Push API key and try again.` - ) -} - -const data = tool.generate() -const json = JSON.stringify(data) - -const opts = { - port: 443, - method: "POST", - host: "appsignal.com", - path: "/diag", - headers: { - "Content-Type": "application/json", - "Content-Length": json.length - }, - cert: fs.readFileSync(path.resolve(__dirname, "../cert/cacert.pem"), "utf-8") -} - -if (shouldSendReport) { - const req = https.request(opts, res => { - res.setEncoding("utf8") - - // print token to the console - res.on("data", chunk => { - const { token } = JSON.parse(chunk.toString()) - console.log("Your diagnose token is:", token) - console.log( - `👀 View this report: https://appsignal.com/diagnose/${token}` - ) - }) - }) - - req.on("error", e => { - console.error(`Problem with diagnose request: ${e.message}`) - }) - - // Write data to request body - req.write(json) - req.end() -} - -console.log(util.inspect(data, { depth: null, colors: true }), "\n") -console.log("✅ Done!") +const { Diagnose } = require("../dist/cli/diagnose") +const diagnose = new Diagnose() +diagnose.run() diff --git a/packages/nodejs/src/cli/diagnose.ts b/packages/nodejs/src/cli/diagnose.ts new file mode 100644 index 00000000..0548e6f1 --- /dev/null +++ b/packages/nodejs/src/cli/diagnose.ts @@ -0,0 +1,248 @@ +const { DiagnoseTool } = require("../diagnose") +const fs = require("fs") +const https = require("https") +const path = require("path") +const util = require("util") +const readline = require("readline") + +export class Diagnose { + public run() { + const data = new DiagnoseTool({}).generate() + + console.log(`AppSignal diagnose`) + console.log(`=`.repeat(80)) + console.log(`Use this information to debug your configuration.`) + console.log(`More information is available on the documentation site.`) + console.log(`https://docs.appsignal.com/`) + console.log(`Send this output to support@appsignal.com if you need help.`) + console.log(`=`.repeat(80)) + + this.print_newline() + + console.log(`AppSignal library`) + console.log(` Language: Node.js`) + console.log(` Package version: ${data["library"]["package_version"]}`) + console.log(` Agent version: ${data["library"]["agent_version"]}`) + console.log( + ` Extension loaded: ${this.format_value( + data["library"]["extension_loaded"] + )}` + ) + + this.print_newline() + + console.log(`Extension installation report`) + console.log(` Installation result`) + console.log(` Status: success`) + console.log(` Language details`) + console.log(` Node.js version: ${data["host"]["language_version"]}`) + console.log(` Download details`) + console.log( + ` Download URL: ${data["installation"]["download"]["download_url"]}` + ) + console.log(` Checksum: ${data["installation"]["download"]["checksum"]}`) + console.log(` Build details`) + console.log(` Install time: ${data["installation"]["build"]["time"]}`) + console.log( + ` Architecture: ${data["installation"]["build"]["architecture"]}` + ) + console.log(` Target: ${data["installation"]["build"]["target"]}`) + console.log( + ` Musl override: ${data["installation"]["build"]["musl_override"]}` + ) + console.log( + ` Linux ARM override: ${data["installation"]["build"]["linux_arm_override"]}` + ) + console.log( + ` Library type: ${data["installation"]["build"]["library_type"]}` + ) + console.log(` Host details`) + console.log(` Root user: ${data["installation"]["host"]["root_user"]}`) + console.log( + ` Dependencies: ${this.format_value( + data["installation"]["host"]["dependencies"] + )}` + ) + + this.print_newline() + + console.log(` Host information`) + console.log(` Architecture: ${data["host"]["architecture"]}`) + console.log(` Operating System: ${data["host"]["os"]}`) + console.log(` Node.js version: ${data["host"]["language_version"]}`) + console.log(` Root user: ${this.format_value(data["host"]["root"])}`) + console.log( + ` Running in container: ${this.format_value( + data["host"]["running_in_container"] + )}` + ) + + this.print_newline() + + console.log(`Configuration`) + console.log( + ` Environment: ${this.format_value(data["config"]["options"]["env"])}` + ) + + console.log( + ` debug: ${this.format_value(data["config"]["options"]["debug"])}` + ) + + console.log(` log: ${this.format_value(data["config"]["options"]["log"])}`) + + console.log( + ` endpoint: ${this.format_value(data["config"]["options"]["endpoint"])}` + ) + console.log( + ` ca_file_path: ${this.format_value( + data["config"]["options"]["ca_file_path"] + )}` + ) + console.log( + ` active: ${this.format_value(data["config"]["options"]["active"])}` + ) + console.log( + ` push_api_key: ${this.format_value( + data["config"]["options"]["push_api_key"] + )}` + ) + + this.print_newline() + + console.log(`Read more about how the diagnose config output is rendered`) + console.log(`https://docs.appsignal.com/nodejs/command-line/diagnose.html`) + + this.print_newline() + + console.log(`Validation`) + console.log( + ` Validating Push API key: ${this.colorize( + data["validation"]["push_api_key"] + )}` + ) + + this.print_newline() + + console.log(`Paths`) + + var contents = data["paths"]["appsignal.log"]["content"] + + console.log(` Current working directory`) + console.log( + ` Path: ${this.format_value(data["paths"]["working_dir"]["path"])}` + ) + + this.print_newline() + + console.log(` Log directory`) + console.log( + ` Path: ${this.format_value(data["paths"]["log_dir_path"]["path"])}` + ) + + this.print_newline() + + console.log(` AppSignal log`) + console.log( + ` Path: ${this.format_value(data["paths"]["appsignal.log"]["path"])}` + ) + console.log(` Contents \(last 9 lines\):`) + console.log(contents.slice(contents.length - 10).join("\n")) + + console.log(`Diagnostics report`) + console.log(` Do you want to send this diagnostics report to AppSignal?`) + console.log(` If you share this report you will be given a link to`) + console.log(` AppSignal.com to validate the report.`) + console.log(` You can also contact us at support@appsignal.com`) + console.log(` with your support token.`) + + this.print_newline() + + if (process.argv.includes("--no-send-report")) { + console.log( + ` Not sending report. (Specified with the --no-send-report option.)` + ) + } else if (process.argv.includes("--send-report")) { + this.send_report(data) + } else { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }) + + let self = this + rl.question( + ` Send diagnostics report to AppSignal? (Y/n): `, + function (answer: String) { + switch (answer || "y") { + case "y": + self.send_report(data) + break + + default: + console.log(` Not sending diagnostics information to AppSignal.`) + } + + rl.close() + } + ) + } + } + + send_report(data: object) { + const json = JSON.stringify(data) + + const opts = { + port: 443, + method: "POST", + host: "appsignal.com", + path: "/diag", + headers: { + "Content-Type": "application/json", + "Content-Length": json.length + }, + cert: fs.readFileSync( + path.resolve(__dirname, "../../cert/cacert.pem"), + "utf-8" + ) + } + + const req = https.request(opts, (res: any) => { + res.setEncoding("utf8") + + // print token to the console + res.on("data", (chunk: any) => { + const { token } = JSON.parse(chunk.toString()) + console.log(` Your support token:`, token) + console.log( + ` View this report: https://appsignal.com/diagnose/${token}` + ) + }) + }) + + req.on("error", (e: any) => { + console.error(`Problem with diagnose request: ${e.message}`) + }) + + req.write(json) + req.end() + } + + print_newline() { + console.log(``) + } + + format_value(value: any) { + return util.inspect(value) + } + + colorize(value: string) { + switch (value) { + case "invalid": + return `\x1b[31minvalid\x1b[0m` + case "valid": + return `\x1b[32mvalid\x1b[0m` + default: + return value + } + } +} diff --git a/test/integration/diagnose b/test/integration/diagnose new file mode 160000 index 00000000..38469f84 --- /dev/null +++ b/test/integration/diagnose @@ -0,0 +1 @@ +Subproject commit 38469f8422e42b64b935614d66f81b08294ac25e