├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ ├── checks.yml │ └── depsbot.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── assets ├── example.cast └── example.svg ├── colors.ts ├── debug.ts ├── deps.ts ├── examples └── hello.ts ├── format.ts └── mod.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = space 7 | indent_size = 2 8 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: denosaurs 2 | github: denosaurs 3 | -------------------------------------------------------------------------------- /.github/workflows/checks.yml: -------------------------------------------------------------------------------- 1 | name: check 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout sources 10 | uses: actions/checkout@v2 11 | 12 | - name: Setup latest deno version 13 | uses: denolib/setup-deno@v2 14 | with: 15 | deno-version: v1.x 16 | 17 | - name: Run deno fmt 18 | run: deno fmt --check 19 | 20 | - name: Run deno lint 21 | run: deno lint --unstable 22 | 23 | test: 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Checkout sources 27 | uses: actions/checkout@v2 28 | 29 | - name: Setup latest deno version 30 | uses: denolib/setup-deno@v2 31 | with: 32 | deno-version: v1.x 33 | 34 | - name: Run deno test 35 | run: deno test --allow-none 36 | -------------------------------------------------------------------------------- /.github/workflows/depsbot.yml: -------------------------------------------------------------------------------- 1 | name: depsbot 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | schedule: 9 | - cron: "0 0 */2 * *" 10 | 11 | jobs: 12 | run: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout Repository 16 | uses: actions/checkout@v2 17 | 18 | - name: Run depsbot 19 | uses: denosaurs/depsbot@master 20 | with: 21 | github_token: ${{ secrets.GITHUB_TOKEN }} 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS files 2 | .DS_Store 3 | .cache 4 | 5 | # IDE 6 | .vscode 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog], 6 | and this project adheres to [Semantic Versioning]. 7 | 8 | ## [0.2.0] - 2020-09-15 9 | 10 | ### Bug Fixes 11 | 12 | - deno 1.4.0 support ([`a111891`]) 13 | 14 | ## [0.1.1] - 2020-09-13 15 | 16 | ### Features 17 | 18 | - deno.land/x release ([`4c11d60`]) 19 | 20 | ## [0.1.0] - 2020-09-13 21 | 22 | ### Features 23 | 24 | - debug ([`1acbdd5`]) 25 | 26 | ### Bug Fixes 27 | 28 | - remove withoutEnv default allow-all ([`32622f2`]) 29 | 30 | [keep a changelog]: https://keepachangelog.com/en/1.0.0/ 31 | [semantic versioning]: https://semver.org/spec/v2.0.0.html 32 | [0.2.0]: https://github.com/denosaurs/debug/compare/0.1.1...0.2.0 33 | [`a111891`]: https://github.com/denosaurs/debug/commit/a1118913fecda01aeb46b97a4659761271d27077 34 | [0.1.1]: https://github.com/denosaurs/debug/compare/0.1.0...0.1.1 35 | [`4c11d60`]: https://github.com/denosaurs/debug/commit/4c11d60d3be77f188376b1dcd57cff1254c41b7e 36 | [0.1.0]: https://github.com/denosaurs/debug/compare/0.1.0 37 | [`1acbdd5`]: https://github.com/denosaurs/debug/commit/1acbdd5206aef12d4b3922a76a2123a2a0be6b17 38 | [`32622f2`]: https://github.com/denosaurs/debug/commit/32622f2104e34f409081f6f9aead3a78879103eb 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-present the denosaurs team 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # debug 2 | 3 | [![Tags](https://img.shields.io/github/release/denosaurs/debug)](https://github.com/denosaurs/debug/releases) 4 | [![CI Status](https://img.shields.io/github/workflow/status/denosaurs/debug/check)](https://github.com/denosaurs/debug/actions) 5 | [![Dependencies](https://img.shields.io/github/workflow/status/denosaurs/debug/depsbot?label=dependencies)](https://github.com/denosaurs/depsbot) 6 | [![License](https://img.shields.io/github/license/denosaurs/debug)](https://github.com/denosaurs/debug/blob/master/LICENSE) 7 | 8 |

9 |
10 | 11 |
12 |

13 | 14 | ```typescript 15 | import { debug } from "https://deno.land/x/debug/mod.ts"; 16 | 17 | const log = debug("app"); 18 | const name = "My Awesome App" 19 | 20 | const app = new Application(); 21 | 22 | app.use((ctx) => { 23 | log("%s %s", ctx.request.method, ctx.request.url.pathname); 24 | ctx.response.body = "Hello World!"; 25 | }); 26 | 27 | log("Starting %s...", name); 28 | await app.listen({ port: 8000 }); 29 | ``` 30 | 31 | ``` 32 | $ DEBUG=worker deno run --allow-env script.ts 33 | ``` 34 | 35 | ## Namespace Colors 36 | 37 | Every debug instance has a color generated for it based on its namespace name. 38 | This helps when visually parsing the debug output to identify which debug instance 39 | a debug line belongs to. 40 | 41 | ## Millisecond diff 42 | 43 | When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls. 44 | 45 | ## Wildcards 46 | 47 | The `*` character may be used as a wildcard. Suppose for example your library has 48 | debuggers named "connect:bodyParser", "connect:compress", "connect:session", 49 | instead of listing all three with 50 | `DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do 51 | `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`. 52 | 53 | You can also exclude specific debuggers by prefixing them with a "-" character. 54 | For example, `DEBUG=*,-connect:*` would include all debuggers except those 55 | starting with "connect:". 56 | 57 | ## Formatters 58 | 59 | Debug uses [printf-style](https://wikipedia.org/wiki/Printf_format_string) formatting. 60 | Below are the officially supported formatters: 61 | 62 | | Formatter | Representation | 63 | |-----------|----------------| 64 | | `%O` | Pretty-print an Object on multiple lines. | 65 | | `%o` | Pretty-print an Object all on a single line. | 66 | | `%s` | String. | 67 | | `%d` | Number (both integer and float). | 68 | | `%j` | JSON. Replaced with the string '[Circular]' if the argument contains circular references. | 69 | | `%%` | Single percent sign ('%'). This does not consume an argument. | 70 | 71 | ## Maintainers 72 | 73 | - Filippo Rossi ([@qu4k](https://github.com/qu4k)) 74 | 75 | ## Other 76 | 77 | ### Related 78 | 79 | - [debug](https://github.com/visionmedia/debug) - A tiny JavaScript debugging utility modelled after Node.js core's debugging technique. 80 | 81 | ### Contribution 82 | 83 | Pull request, issues and feedback are very welcome. Code style is formatted with `deno fmt` and commit messages are done following Conventional Commits spec. 84 | 85 | ### Licence 86 | 87 | Copyright 2020-present, the denosaurs team. All rights reserved. MIT license. 88 | -------------------------------------------------------------------------------- /assets/example.cast: -------------------------------------------------------------------------------- 1 | {"version": 2, "width": 105, "height": 41, "timestamp": 1599955843, "env": {"SHELL": "/bin/zsh", "TERM": "xterm-256color"}} 2 | [0, "o", "$ DEBUG=app deno run --allow-env script.ts\r\n"] 3 | [0.273927, "o", "\u001b[34mapp\u001b[39m Starting My Awesome App... \u001b[34m+0ms\u001b[39m\r\n"] 4 | [0.755154, "o", "\u001b[34mapp\u001b[39m GET /hello \u001b[34m+583ms\u001b[39m\r\n"] 5 | [1.266938, "o", "\u001b[34mapp\u001b[39m GET /world \u001b[34m+511ms\u001b[39m\r\n"] 6 | [1.78001, "o", "\u001b[34mapp\u001b[39m GET /debug \u001b[34m+514ms\u001b[39m\r\n"] 7 | [2.293136, "o", "\u001b[34mapp\u001b[39m GET /is \u001b[34m+513ms\u001b[39m\r\n"] 8 | [2.804962, "o", "\u001b[34mapp\u001b[39m GET /awesome \u001b[34m+511ms\u001b[39m\r\n"] 9 | [5.644236, "o", "^C"] 10 | -------------------------------------------------------------------------------- /assets/example.svg: -------------------------------------------------------------------------------- 1 | $DEBUG=appdenorun--allow-envscript.tsappStartingMyAwesomeApp...+0msappGET/hello+583msappGET/world+511msappGET/debug+514msappGET/is+513msappGET/awesome+511ms^C -------------------------------------------------------------------------------- /colors.ts: -------------------------------------------------------------------------------- 1 | import { colors } from "./deps.ts"; 2 | 3 | export type ColorFunction = (message: string) => string; 4 | export const colorFunctions: ColorFunction[] = [ 5 | colors.red, 6 | colors.green, 7 | colors.yellow, 8 | colors.blue, 9 | colors.magenta, 10 | colors.cyan, 11 | ]; 12 | 13 | function hashCode(s: string): number { 14 | let h = 0; 15 | let l = s.length; 16 | let i = 0; 17 | if (l > 0) while (i < l) h = ((h << 5) - h + s.charCodeAt(i++)) | 0; 18 | return h; 19 | } 20 | 21 | export function generateColor(message: string): ColorFunction { 22 | const hash = Math.abs(hashCode(message)); 23 | return colorFunctions[hash % colorFunctions.length]; 24 | } 25 | -------------------------------------------------------------------------------- /debug.ts: -------------------------------------------------------------------------------- 1 | import { generateColor, ColorFunction } from "./colors.ts"; 2 | import { encode } from "./deps.ts"; 3 | import { format } from "./format.ts"; 4 | 5 | export interface Debug { 6 | (fmt: string, ...args: unknown[]): void; 7 | self: Debugger; 8 | } 9 | 10 | export class Debugger { 11 | manager: DebugManager; 12 | ns: string; 13 | color: ColorFunction; 14 | last: number; 15 | enabled: boolean; 16 | 17 | constructor(manager: DebugManager, namespace: string) { 18 | this.manager = manager; 19 | this.ns = namespace; 20 | this.color = generateColor(namespace); 21 | this.last = 0; 22 | this.enabled = manager.enabled.some((r) => r.test(namespace)); 23 | } 24 | 25 | log(fmt: string, ...args: unknown[]): void { 26 | if (!this.enabled) return; 27 | const diff = Date.now() - (this.last || Date.now()); 28 | fmt = format(fmt, ...args); 29 | const msg = `${this.color(this.ns)} ${fmt} ${this.color(`+${diff}ms`)}\n`; 30 | Deno.stderr.writeSync(encode(msg)); 31 | this.last = Date.now(); 32 | } 33 | } 34 | 35 | class DebugManager { 36 | debuggers: Map; 37 | enabled: RegExp[]; 38 | 39 | constructor(enabled?: RegExp[]) { 40 | this.debuggers = new Map(); 41 | this.enabled = enabled ?? []; 42 | } 43 | } 44 | 45 | function extract(opts?: string): RegExp[] { 46 | if (!opts || opts.length === 0) return []; 47 | opts = opts.replace(/\s/g, "").replace(/\*/g, ".+"); 48 | return opts.split(",").map((rule) => new RegExp(`^${rule}$`)); 49 | } 50 | 51 | let manager: DebugManager; 52 | 53 | export function withoutEnv(enabled?: RegExp[] | string) { 54 | if (!enabled) enabled = []; 55 | if (typeof enabled === "string") enabled = extract(enabled); 56 | manager = new DebugManager(enabled); 57 | } 58 | 59 | export function debug(namespace: string): Debug { 60 | if (!manager) manager = new DebugManager(extract(Deno.env.get("DEBUG"))); 61 | 62 | const dbg = new Debugger(manager, namespace); 63 | manager.debuggers.set(namespace, dbg); 64 | const de: Debug = Object.assign(dbg.log.bind(dbg), { 65 | self: dbg, 66 | }); 67 | return de; 68 | } 69 | -------------------------------------------------------------------------------- /deps.ts: -------------------------------------------------------------------------------- 1 | export { createHash } from "https://deno.land/std@0.69.0/hash/mod.ts"; 2 | export { encode } from "https://deno.land/std@0.69.0/encoding/utf8.ts"; 3 | export * as colors from "https://deno.land/std@0.69.0/fmt/colors.ts"; 4 | -------------------------------------------------------------------------------- /examples/hello.ts: -------------------------------------------------------------------------------- 1 | import { debug } from "../mod.ts"; 2 | 3 | const log = debug("app"); 4 | const name = "My Awesome App"; 5 | 6 | log("Starting %s...", name); 7 | -------------------------------------------------------------------------------- /format.ts: -------------------------------------------------------------------------------- 1 | export function format(f: string, ...args: unknown[]) { 2 | let i = 0; 3 | let len = args.length; 4 | let str = String(f).replace(/%[sdjoO%]/g, (x: string): string => { 5 | if (x === "%%") return "%"; 6 | if (i >= len) return x; 7 | switch (x) { 8 | case "%s": 9 | return String(args[i++]); 10 | case "%d": 11 | return Number(args[i++]).toString(); 12 | case "%o": 13 | return Deno.inspect(args[i++]).split("\n").map((_) => _.trim()).join( 14 | " ", 15 | ); 16 | case "%O": 17 | return Deno.inspect(args[i++]); 18 | case "%j": 19 | try { 20 | return JSON.stringify(args[i++]); 21 | } catch { 22 | return "[Circular]"; 23 | } 24 | default: 25 | return x; 26 | } 27 | }); 28 | for (const x of args.splice(i)) { 29 | if (x === null || !(typeof x === "object" && x !== null)) { 30 | str += " " + x; 31 | } else { 32 | str += " " + Deno.inspect(x); 33 | } 34 | } 35 | return str; 36 | } 37 | -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | export * from "./debug.ts"; 2 | --------------------------------------------------------------------------------