{"id":15603374,"url":"https://github.com/codemeasandwich/scribbles","last_synced_at":"2025-04-24T07:39:54.703Z","repository":{"id":34983133,"uuid":"193903276","full_name":"codemeasandwich/scribbles","owner":"codemeasandwich","description":"scribbles is a log and tracing lib for Node","archived":false,"fork":false,"pushed_at":"2023-05-08T21:55:34.000Z","size":202,"stargazers_count":10,"open_issues_count":18,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-24T23:51:22.624Z","etag":null,"topics":["console","logging","nodejs","tracking"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/codemeasandwich.png","metadata":{"files":{"readme":"ReadMe.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-06-26T12:49:27.000Z","updated_at":"2024-01-12T18:09:35.000Z","dependencies_parsed_at":"2023-02-16T18:01:57.826Z","dependency_job_id":null,"html_url":"https://github.com/codemeasandwich/scribbles","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codemeasandwich%2Fscribbles","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codemeasandwich%2Fscribbles/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codemeasandwich%2Fscribbles/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codemeasandwich%2Fscribbles/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/codemeasandwich","download_url":"https://codeload.github.com/codemeasandwich/scribbles/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242432357,"owners_count":20127384,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["console","logging","nodejs","tracking"],"created_at":"2024-10-03T03:02:59.399Z","updated_at":"2025-03-07T17:31:29.475Z","avatar_url":"https://github.com/codemeasandwich.png","language":"JavaScript","funding_links":["https://www.buymeacoffee.com/codemeasandwich"],"categories":[],"sub_categories":[],"readme":"# Scribbles\n\n**scribbles** a log and tracing lib for Node [![npm version](https://badge.fury.io/js/scribbles.svg)](https://www.npmjs.com/package/scribbles)\n\n---\n\n### Scribbles has some nice features.\n\n* **[Customised output](#how-to-customise-log-output)**\n* **[Tracing logs](#how-to-trace-logs)**\n  * All logs with [`.trace(`](#trace-function-signature) will be **automatically** tagged, no matter where in your app it is.\n  * use `traceTrigger` to filter a set of logs to only when there is a threshold is met or exceeded\n  * Can trace incoming requests with the [w3c trace-context](https://www.w3.org/TR/trace-context/) headers\n  * scribbles will attempt to intercept all outgoing requests and inject the headers :sunglasses:\n  * Can **automatically** inject IDs into outgoing headers. Works with [axios](https://www.npmjs.com/package/axios#axios), [request](https://www.npmjs.com/package/request) \u0026 [http](https://nodejs.org/api/http.html#http_http_get_url_options_callback)/[https](https://nodejs.org/api/https.html#https_https_get_url_options_callback)\n* **More insight in your logs**\n  * Git repository name\n  * Current branch\n  * Last commit hash\n  * Environment: local / dev / prod\n* **Static source code analysis**\n  * Resolve the calling location **without** the expensive of a stacktrace\n  * Can load map file to report the correct source file and line\n    *  Helpful when your Service is build/bundled\n    *  Deploying **Lambda** style functions\n* **[Generate performance reports](#performance-monitoring)**\n  * Detailed metrics on service and host\n  * Flag when the eventloop is blocking. This can happen when your app is over-loaded.\n\n## **scribbles** is 100% agnostic. **Connect your logging service of choice** to either: `stdOut` to get a string of the entry OR `dataOut` to get an enriched object representing the log event\n\n### If you like it, [★ it on github](https://github.com/codemeasandwich/scribbles), [![Buy me a coffee](https://img.shields.io/badge/buy%20me-a%20coffee-orange.svg)](https://www.buymeacoffee.com/codemeasandwich) and/or share :beers:\n\n## How to install\n\nYou should be running **node v8.5.0+**\n\n```\nnpm install --save scribbles\n```\n\n```\nyarn add scribbles\n```\n\n## How to use\n\n```js\nconst scribbles = require('scribbles');\n\nscribbles.log(\"hello world\")\n\n// myRepo:local:master [ ] 2022-06-27T16:24:06.473 #3d608bf \u003clog\u003e index.js:174 hello world\n```\n\n## Logging signature\n\n```\nscribbles[logLevel](message, [value, [error]])\n```\n\n## How to customise\n\nThere is a `config` that takes a configuration object.\n\n* **stdOut** [function] - *defaults: `console`*\n  * Redirect the string output of the log entry\n* **dataOut**  [function]\n  * A callback to receive an object representing the log entry\n* **stringify**  [function]\n  * A function used in stdOut to parsing for values into a string\n* **mode** [string] - *default: 'dev'*\n  * Can use NODE_ENV from environment variables\n* **format** [string] - *defaults: \"{repo}:{mode}:{branch} [{spanLabel} {spanId}] {time} #{hash} \u003c{logLevel}\u003e {fileName}:{lineNumber} {message} {value} {stackTrace}\"*\n  * git:\n    * `repo`: The git repository name as it appears on the origin\n    * `branch`: The current git branch\n    * `hash`: Short git hash of current commit\n  * trace:  \n    * `traceId`: Used for distributed tracing in microservices [**[more](https://www.w3.org/TR/trace-context/#trace-id)**]\n    * `spanId`: the execution of a client call [**[more](https://www.w3.org/TR/trace-context/#parent-id)**]\n    * `span64`: the base64 encoded version of `spanId`\n    * `spanLabel`: A label to identify this trace\n    * `tracestate`: Ordered list of key/value hops\n  * info:\n    * `time`: Time of logging\n    * `logLevel`: The logging level for this entry\n    * `hostname`: The hostname of the operating system.\n    * `instance`: a base16 value representing the current instance\n    * `mode`: The environment your application is running in. *e.g. local, dev, prod etc..*\n  * context:\n    * `fileName`: The file name\n    * `lineNumber`: The line in the file\n  * input:  \n    * `message`: Message to log\n    * `value`: Values to log\n    * `stackTrace`: The stack trace if an Error object was passed\n  * `v`: The version of scribbles used to create this entry. This allows matching log body with parsers. As the layout may change with new versions.\n* **time** [string] - *defaults: \"YYYY-MM-DDTHH:mm:ss.SSS\"*\n  * [Time formatting is provided by Moment.js](https://momentjs.com/docs/#/displaying/format/)\n* **traceTrigger** [string] *\n  * Used within trace call.\n  * Log events will be stored and only push out if the scribbles[logLevel] \u003e= the traceTrigger level\n* **logLevel** [string] - *defaults: \"debug\"*\n  * Report on this level and higher\n  * Can use LOG_LEVEL from environment variables\n* **levels** [array] - *defaults: `[\"error\", \"warn\", \"status\", \"log\", \"timer\", \"info\", \"debug\"]`*\n  * Messages will be filtered from the `logLevel` to the start of the array\n  * These log levels will also be available as functions on scribbles\n* **headers** [string/array/null]\n  * **activated when using [scribbles.middleware...](#tracing-across-your-micro-services)**\n  * `string` of a header name to forward\n  * `RegExp` to match header names to forward\n  * `array` of header names to forward. Can exact matching `string`s and/or `RegExp`s.\n  * `null` to disable forwarding headers\n* **headersMapping** [object:[array/string]]\n  * An `object` of output keys with input selector values.\n  * Values can be A `string` OR `array of strings`: that will be taken in order of preference to be selected(i.e. If an incoming request has a header named the same as first index. Ues that, else check the next index and so on)\n* **gitEnv** [Object] - the **attribute names** at the **end** of their `process.env`\n  * `hash`: attribute name of git short hash\n  * `repo`: attribute name of git repository name\n  * `branch`: attribute name of git branch\n* **pretty** [Object]\n  * `inlineCharacterLimit`[number]: Will inline values up to this length. ~ In Dev Mode, this will be set to the width of your terminal **or** _default to_ `80`\n  * `indent`[string]: preferred indentation. _Defaults  `\"  \"` ~ 2 spaces_\n  * `depth`[number]: This represents how many nested steps in the object/array tree are to be walked\n  * `singleQuotes`[string]: Set to true to get single-quoted strings. _Default: `false`_\n  * `filter`(object, key) [function]: Expected to return a boolean of whether to include the property in the output.\n  * `transform`(object, key, val) [function]: Expected to return a string that transforms the string that resulted from stringifying a given property.\n    * This can be used to detect special types of objects that need to be stringified in a particular way, or to return an alternate string in this case. e.g. given a field named \"password\" return \"****\"\n---\n\n### Example:\n\nVia **package.json**\n\nJust add a \"scribbles\" attribute\n\n```json\n{\n  \"name\": \"myrepo\",\n  \"version\": \"0.0.0\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" \u0026\u0026 exit 1\"\n  },\n  \"dependencies\": {\n\n  },\n  \"scribbles\":{\n     \"mode\":\"test-runner\",\n     \"logLevel\":\"warn\",\n     \"levels\":[\"danger\", \"error\", \"warn\", \"log\", \"info\", \"debug\"],\n     \"format\":\"{time} [{mode}#{hash}] \u003c{logLevel}\u003e {message}\"\n  }\n}\n```\n\nVia **.config(...) function**\n\n```js\nscribbles.config({\n   mode:'test-runner',\n   logLevel:\"warn\", //only output warning messages or higher\n   levels:[\"danger\", \"error\", \"warn\", \"log\", \"info\", \"debug\"],\n   format:'{time} [{mode}#{hash}] \u003c{logLevel}\u003e {message}'\n})\n\nscribbles.danger(\"hello world\")\n\n// 2022-06-27T16:24:06.473 [test-runner#3d608bf] \u003cdanger\u003e hello world\n```\n\n## dataOut\n\nThere is also a option in config to set the `dataOut` that will receive an object representing the log entry.\n\n```js\nscribbles.config({\n   dataOut:function(data){\n     console.log(data);\n   }\n})\n\nscribbles.log(\"hello world\")\n\n/*{\n   v:\"1.2.3\",\n   git:{\n      repo:\"myRepo\",\n      branch:\"master\",\n      hash:\"3d608bf\"\n   },\n   trace:{\n      ...\n   }\n   info:{\n      time:2022-06-27T16:24:06.473Z,\n      mode:\"local\",\n      hostname:\"box\",\n      logLevel:\"log\",\n      instance:\"bfc977a\"\n   },\n   context:{\n      fileName:\"index.js\",\n      lineNumber:174\n   },\n   input:{\n    message: \"hello world\"\n   },\n}*/\n```\n\n---\n\n# performance monitoring\n\n:rocket: You can also poll the performance of your service. By calling `scribbles.status(...)`\n\nThis will attach an additional attribute to the **dataOut** and will not be available in *stdOut*.\n\n* status:\n  * `state`: the state of the services. e.g. *\"up\"*, *\"blocking\"*\n  * `cpu`: CPU info\n    * `cores`: number of available cores\n    * `model`: description of the processor\n    * `speed`: MHz frequency speed\n    * `percUsed`: load on process as percentage\n    * `percFree`: available on process as percentage\n  * `sys`: System info\n    * `startedAt`: when the system was started\n    * `arch`: platform architecture. e.g \"x64\"\n    * `platform`: the operating system platform\n    * `totalMem`: the total megabytes of memory being used\n    * `freeMem`: the total megabytes of memory free\n    * `usedMem`: the total megabytes of memory being used\n  * `process`: Node process info\n    * `percUsedCpu`: the percentage of processing power being used by this process\n    * `percFreeMem`: the percentage of memory being used by this process\n    * `usedMem`: the total megabytes of memory being used by this process\n    * `startedAt`: when it's process was started\n    * `pTitle`: the current process title (i.e. returns the current value of ps)\n    * `pid`: the ID of the process\n    * `ppid`: the ID of the current parent process\n    * `user`: node the name of the user who started node\n    * `vNode`: version of node\n  * `network`: Networking info\n    * `port`: listening on this Port\n    * `connections`: number of current established connections\n\n\n### Example:\n\n```js\nscribbles.config({\n   dataOut:console.log\n})\n\nsetInterval(function(){\n  scribbles.status();\n}, 5000);\n```\n\n*This will give you a performance snapshot every 5 seconds.*\n\n---\n\n## Using webpack and GIT but only deploying the bundle?\n\n**You can get Scribbles to store the Git info at build time, in order to show in the run time logs**\n\nVia **webpack.config.js**\n\nHere is how to add git status to you logs.\n\n```js\n//...\nconst ScribblesWithGitInBundle = require('scribbles/gitStatus');\n//...\nmodule.exports = {\n//...\n  plugins: [\n    ScribblesWithGitInBundle,\n//  ...\n  ]\n//...\n};\n```\nThat's it!\n\n#### If your **git** values are passed by the `process.env`.\n\nYou can select then via the config opts.\n\nVia **package.js**\n\n```json\n{\n  ...,\n  \"scribbles\":{\n     ...,\n     \"gitEnv\": {\n        \"hash\":\"GITHUB_SHA\",\n        \"repo\":\"GITHUB_REPOSITORY\",\n        \"branch\":\"GITHUB_REF\"\n     }\n  }\n}\n```\n\n**Tip**: If you are using **Heroku**, the git hash is storted in the *\"SOURCE_VERSION\"*\n\n---\n\n## Using TypeScript in a bundle?\n\nTo get accurate file \u0026 line references when transpiling your code.\nYou will need to enable inlining sourceMaps.\nThere are two steps you need to add.\n\n1. **Generate source-map as part of the build**\n    * In your `tsconfig.json` add `\"sourceMap\": true` under \"*compilerOptions*\"\n\n2. **Enable sourceMap support in Node**\n    * **Node v12.12+** : can just add the ` --enable-source-maps ` flag to the node command\n\n      OR\n    * **Node older** : Will need to install **[NPM-\u003esource-map-support](https://www.npmjs.com/package/source-map-support)** and add `require('source-map-support').install()` to the top for you service\n\n---\n\n## How to analyzing performance of pieces of your code\n\nYou can start a timer to calculate the duration of a specific operation. To start one, call the `scribbles.timer(tag,[message])` function, giving it a name and an optional message. To stop the timer, just call the `scribbles.timerEnd(tag,[message])` function, again passing the timer's name as the first parameter.\n\nThere are 3 functions:\n\n1. `scribbles.timer()` - Starts and/or logs the current value a timer based on the tag passed to the function.\n3. `scribbles.timerEnd()` - logs the current value a timer and removes it, to be used later.\n\n```js\nscribbles.timer(\"Yo\")\nsetTimeout(()=\u003e{\n  scribbles.timer(\"Yo\",\"123\")\n  setTimeout(()=\u003e{\n    scribbles.timerEnd(\"Yo\",\"done!\")\n  }, 300)\n}, 400)\n```\nOutput: You see the time it took to run the code\n```cli\nmyRepo:local:master [ ] 2022-06-27T13:04:52.133 \u003ctimer\u003e app.js:18 Yo (+0.00ms|0.00ms)  \nmyRepo:local:master [ ] 2022-06-27T13:04:52.552 \u003ctimer\u003e app.js:20 Yo:123 (+419.40ms|419.40ms)  \nmyRepo:local:master [ ] 2022-06-27T13:04:52.875 \u003ctimerEnd\u003e app.js:22 Yo:done! (+323.21ms|742.61ms)\n```\n\n---\n\n## How to trace logs\n\nWhen trying to debug a problem with logs that are intertwined. A stacktrace will give you limited information. You can see where the Error occurred and a message. However you cannot see the values as it flow through your system.\n\nUsing the trace system. Each log entry will be able to be connected, as it flows through your system.\n\nTo use the trace system you only need to pass in a root function that acts as the start of the execution. Everything that is executed within this function will be matched to the same trace ID, and name if provided.\n\n## trace function signature\n\n```\nscribbles.trace([label/opt,]next_fu)\n```\n\nThe first argument to can be an options object. Here you can specify a `spanLabel` to tag your entries, a`traceId` \u0026 the `tracestate` that are using in distributed tracing.\n\n### Tracing a path within your service\n\n*index.js*\n```js\n// for fun lets set a custom format for our logs\nscribbles.config({format:`[{traceName} {spanId}] {message}`})\n\n// an example of an event handler\nfunction incoming(dataIn){\n  // wrap the work we want to do in `scribbles.trace`\n  scribbles.trace(\"eventstream\",(spanId)=\u003e{\n\n    // Event message logged from with here will have this correlation ID\n    // spanId = 090e8e40000005\n\n    workToDo(dataIn); // kick of the work\n  })\n}\n```\n\n*eventstream.js*\n```js\nfunction workToDo(dataIn){\n  // ...\n  // user scribbles as normal\n  scribbles.log(\"Doing something with the eventstream\")\n  // [eventstream 090e8e40000005] Doing something with the eventstream\n  // ...\n}\n```\n\n### Filter Tracing logs to **only when there is a problem**.\n\nLog just the timeline of events you care about.\n\nBy setting a \"traceTrigger\" level. All scribbles calls **within** a trace context will respect it. If a call is make that matchs or exceds this level. Past, current \u0026 new events in this context will be outputted, else they will be suppressed\n\n```js\nscribbles.config({\n  // Any call to error or higher will trigger all logs from logLevel for this specific this execution context\n  traceTrigger:\"error\",\n  // logLevel dont not have to be set. Will default to All\n  logLevel:'warning',\n  // Levels do not need to be set this is only for demonstration purposes\n  levels:['fatal','error','warning','info'],\n})\n\n\nscribbles.trace('in_trace',()=\u003e{\n  scribbles.info(\" --- Will NEVER be shown\") // as this is below the logLevel\n  setTimeout(()=\u003e{\n     // will be store, but will not be sent out at this point\n    scribbles.warning(\" --- Wait\")\n    setTimeout(()=\u003e{\n      // traceTrigger exceded! store event will be sent out + this event\n      scribbles.fatal(\" --- Now!\")\n      setTimeout(()=\u003e{\n        // will be sent out at the traceTrigger was already hit, for this *trace context*\n        scribbles.warning(\" --- More!\")\n      }, 500)\n    }, 500)\n  }, 500)\n})\n\n/*\n  myRepo:local:master [in_trace 3b3c6000000001] 2022-06-27T09:22:52.588 #3d608bf \u003cwarning\u003e app.js:14  --- Wait\n  myRepo:local:master [in_trace 3b3c6000000001] 2022-06-27T09:22:52.890 #3d608bf \u003cfatal\u003e app.js:16  --- Now!  \n  myRepo:local:master [in_trace 3b3c6000000001] 2022-06-27T09:22:53.199 #3d608bf \u003cwarning\u003e app.js:18  --- More!\n*/\n```\n\n### Tracing across your micro-services.\nin accordance with [W3C trace-context](https://www.w3.org/TR/trace-context/)\n\nDistributed tracing is powerful and makes it easy for developers to find the causes of issues in highly-distributed microservices applications, as they track how a single interaction was processed across multiple services. But while tracing has exploded in popularity in recent years, there still isn’t much built-in support for it in languages, web frameworks, load balancers, and other components, which can lead to missing components or broken traces.\n\n🤔 Generating and attaching **trace-context** values to request headers is a standardized way of addressing this problem.\n\nInstrumenting web frameworks, storage clients, application code, etc. to make tracing work out of the box. 🥳\n\n---\n\n### Example: Using [Express](https://expressjs.com/) \u0026 [Axios](https://github.com/axios/axios#axios) within [AWS](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-request-tracing.html)\n\n```js\nconst scribbles = require('scribbles');\nconst axios     = require('axios');\nconst express   = require('express');\n\nscribbles.config({\n  headers:[\"X-Amzn-Trace-Id\"]\n});\n\nconst app = express();\n\n// start a trace for each incoming request.\napp.use(scribbles.middleware.express);\n\napp.get('/', function (req, res){\n\n  scribbles.log(\"incoming\");\n  // myRepo:local:master [198.10.120.12 090e8e40000005] 2022-06-27T16:24:06.473 #3d608bf \u003clog\u003e index.js:174 incoming\n\n  // Just by calling this other service normally, scribbles will inject the tracing headers\n  axios.get('https://some.domain.com/foo/')\n    .then(response =\u003e {\n      scribbles.log(response.data);\n      res.send(\"fin\")\n    })\n    .catch(error =\u003e {\n      scribbles.error(error);\n    });\n\n}) // END app.get '/'\n\n\napp.listen(port, () =\u003e scribbles.status(`App is ready!`))\n```\n\n**Example above is for [axios](https://www.npmjs.com/package/axios) but it will also work with [http](https://nodejs.org/api/http.html#http_http_get_url_options_callback) and [request](https://www.npmjs.com/package/request)**\n\n---\n\n### If you want to spin you own middleware\n\nIt may look something like this\n```js\nfunction traceMiddleware({headers}, res, next){\n  scribbles.trace({\n    // You can pass the traceparent as the traceId\n    // or you can pull the traceId from the traceparent and pass that\n    traceId:headers.traceparent,\n    tracestate:headers.tracestate,\n\n    // lets tag the current trace/span with the caller's IP\n    spanLabel:headers['x-forwarded-for']\n  },(spanId) =\u003e next());\n} // END express\n```\n\n#### if you want to handle the out going headers\n\n```js\napp.get('/', function (req, res){\n\n  scribbles.log(\"incoming\");\n  // myRepo:local:master [198.10.120.12 090e8e40000005] 2022-06-27T16:24:06.473 #3d608bf \u003clog\u003e index.js:174 incoming\n\n\n  axios.get('https://some.domain.com/foo/',{\n      headers:scribbles.trace.headers() // tracing header IDs\n    })\n    .then(response =\u003e {\n      scribbles.log(response.data);\n      res.send(\"fin\")\n    })\n    .catch(error =\u003e {\n      scribbles.error(error);\n    });\n\n}) // END app.get '/'\n```\n\n---\n\n**small print:**\n\n**MIT** - If you use this module(or part), credit it in the readme of your project and failing to do so constitutes an irritating social faux pas. Besides this, do what you want with this code but don't blame me if it does not work.  If you find any problems with this module, [open issue on Github](https://github.com/codemeasandwich/scribbles/issues). However reading the Source Code is suggested for experience JavaScript and node engineer's and may be unsuitable for overly sensitive persons with low self-esteem or no sense of humour. Unless the word tnetennba has been used in it's correct context somewhere other than in this warning, it does not have any legal or grammatical use and may be ignored. No animals were harmed in the making of this module, although the yorkshire terrier next door is living on borrowed time, let me tell you. Those of you with an overwhelming fear of the unknown will be gratified to learn that there is no hidden message revealed by reading this warning backwards, I think.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodemeasandwich%2Fscribbles","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodemeasandwich%2Fscribbles","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodemeasandwich%2Fscribbles/lists"}