{"id":22710580,"url":"https://github.com/smallhelm/json-log","last_synced_at":"2025-08-07T15:31:21.694Z","repository":{"id":33344009,"uuid":"36988809","full_name":"smallhelm/json-log","owner":"smallhelm","description":"write logs as json to stdout and stderr","archived":false,"fork":false,"pushed_at":"2018-11-09T18:44:18.000Z","size":37,"stargazers_count":8,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-11T20:13:01.692Z","etag":null,"topics":["json","logger","logging","nodejs","typescript"],"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/smallhelm.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":"2015-06-06T17:28:40.000Z","updated_at":"2023-05-23T14:47:53.000Z","dependencies_parsed_at":"2022-08-24T16:42:52.467Z","dependency_job_id":null,"html_url":"https://github.com/smallhelm/json-log","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smallhelm%2Fjson-log","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smallhelm%2Fjson-log/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smallhelm%2Fjson-log/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smallhelm%2Fjson-log/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/smallhelm","download_url":"https://codeload.github.com/smallhelm/json-log/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":229057638,"owners_count":18013289,"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":["json","logger","logging","nodejs","typescript"],"created_at":"2024-12-10T12:10:39.768Z","updated_at":"2024-12-10T12:10:40.551Z","avatar_url":"https://github.com/smallhelm.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# json-log\n\n[![Build Status](https://travis-ci.org/smallhelm/json-log.svg)](https://travis-ci.org/smallhelm/json-log)\n[![TypeScript](https://img.shields.io/badge/types-TypeScript-blue.svg)](https://www.typescriptlang.org)\n\nLightweight, robust, fast, and opinionated json logger.\n\nWorks great with systemd, heroku, or lambda.\n\n- [Philosophy](#philosophy)\n  - [Write to stdout/err](#write-to-stdouterr)\n  - [Logs should be meaningful](#logs-should-be-meaningful)\n  - [Context is crucial](#context-is-crucial)\n  - [Robust json stringification](#robust-json-stringification)\n- [API](#api)\n  - [log.info(message, data)](#loginfomessage-data)\n  - [log.warn(message, data)](#logwarnmessage-data)\n  - [log.error(message, data)](#logerrormessage-data)\n  - [log2 = log.child(data)](#log2--logchilddata)\n- [Customize](#customize)\n- [License](#license)\n\n## Philosophy\n\n### Write to stdout/err\n\nLet your process manager (systemd, heroku, lambda etc.) handle writing logs. Persisting logs can get complicated fast; log rotation, file limits, compression, and backups just to name a few.\n\nYour nodejs process should not be burdened with all that complexity. Simply write to stdout/err and let the manager take care of the rest.\n\nI often use systemd with journald+syslog. It works amazing. You can always set up other tools that monitor syslog and pipe them to another service i.e. AWS Cloudwatch.\n\nNOTE: You can [customize](#customize) the write method.\n\n#### pid? hostname?\n\nThe process manager's logger should add info such as the process name, pid and hostname.\n\nNOTE: If needed, you can add them like other context data:\n\n```js\nlog.child({\n  hostname: require(\"os\").hostname(),\n  pid: process.pid\n});\n```\n\n### Logs should be meaningful\n\nLogs should help you monitor the system, alert you of errors, and help you diagnose issues in a post-mortem. They should not be riddled with \"normal\" error messages that drown out the errors that require your attention.\n\nHaving only 3 levels helps you focus and create meaningful, actionable logs.\n\nNOTE: You can [customize](#customize) the levels.\n\n#### 1 - `error`\n\nThe system is unstable and staff should be alerted to take action. A stable system will not emit error logs. This way you take `error` seriously and fix the problem so that condition is gracefully handled in the future. Don't be shy, you should use `log.error` statements generously. But they should only be called when there is an actual error that requires your attention.\n\nExamples:\n\n- A remote service you depend on changed it's api and now responds with unexpected data. Your code now throws an exception trying to parse it.\n- A bad db query was made. You'll want to fix that query so it will work in the future.\n- The db connection was lost and the retries failed.\n\nThese log entries are sent to stderr. You can setup your process manager to do something special with the stderr stream i.e. email them to you, or trigger a monitoring alarm.\n\n#### 2 - `warn`\n\nA recoverable error that is somewhat unexpected. You'll want to monitor these to give you clues when things may become unstable, or detect malicious users.\n\nExamples:\n\n- A user tried uploading a file that exceeded the size limit. Keep an eye on that user id, they may be malicious. (Be sure to include the userId in the log entries so you can correlate their activity)\n- A rpc call had to retry before getting a successful response. May indicate the service or network connection is having trouble.\n- A sub-process was restarted. May be an issue if it's repeatedly restarted.\n\n#### 3 - `info`\n\nAny useful information about the operation of the system. Information that helps you in a post-mortem, or in analysing the usage and performance of the system.\n\nExamples:\n\n- This request was made\n- This task was started\n- This query returned X\n\n#### Why no debug/trace?\n\nDuring development use `console.log` to debug your system. Remove those `console.log`s before deploying to production. Anything that would be helpful in production should use `log.info`.\n\n### Context is crucial\n\nHigh concurrency is easy to achieve in nodejs. Therefore it's important that each entry contains information about its context. What request it is a part of, what parameters its working with etc. Include request ids, urls, user ids, db record ids, anything that helps give context to the log message. `json-log` will robustly serialize them, including error objects.\n\nCreate context specific loggers using `log.child(data)`\n\n### Robust json stringification\n\nThis json encoder gracefully handles the following:\n\n- Circular references\n- Error objects\n- Buffers and Typed Arrays (using util.inspect to truncate them)\n- http req/res objects\n\nSee test.js for more details\n\n## API\n\n```js\nimport log from \"json-log\";\n// or\nvar log = require(\"json-log\").log;\n```\n\n### log.info(message, data)\n\nSimply write a message string along with some optional data to `stdout` at level `3`.\n\n```js\nlog.info(\"hello world\");\n// OUT-\u003e {\"level\":3,\"time\":...,\"msg\":\"hello world\"}\n\nlog.info(\"hello world\", { extra: \"data\", arr: [1, 2] });\n// OUT-\u003e {\"level\":3,\"time\":...,\"extra\":\"data\",\"arr\":[1,2],\"msg\":\"hello world\"}\n\nlog.info(\"hello world\", [1, 2]); // shorthand for {data: [1,2]}\n// OUT-\u003e {\"level\":3,\"time\":...,\"data\":[1,2],\"msg\":\"hello world\"}\n\n// Accidentally included a buffer or typed array in your data? No problem.\nlog.info(\"oops!!\", Buffer.alloc(10000000));\n// OUT-\u003e {\"level\":3,\"time\":...,\"data\":\"\u003cBuffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... \u003e\",\"msg\":\"oops!!\"}\n```\n\n### log.warn(message, data)\n\nSame as `log.info` but level is `2`\n\n```js\nlog.warn(\"wat da?\", { more: \"info\" });\n// OUT-\u003e {\"level\":2,\"time\":...,\"more\":\"info\",\"msg\":\"wat da?\"}\n```\n\n### log.error(message, data)\n\nSame as `log.info` but writes to `stderr` and level is `1`\n\n```js\nlog.error(\"grrr\", { err: new Error(\"fail\") });\nlog.error(\"grrr\", new Error(\"fail\")); // shorthand for {err: ...}\n// ERR-\u003e {\"level\":1,\"time\":...,\"err\":{\"name\":\"Error\",\"message\":\"fail\",\"stack\":\"Error...\"},\"msg\":\"some error\"}\n```\n\n### log2 = log.child(data)\n\nCreate a new log that has the context `data` included on every entry.\n\n```js\nvar log2 = log.child({ reqId: 1, foo: \"bar\" });\n\nlog2.info(\"hello again\");\n// OUT-\u003e {\"level\":3,\"time\":...,\"reqId\":1,\"foo\":\"bar\",\"msg\":\"hello again\"}\n\nlog2.info(\"hello again\", { aaa: 1 });\n// OUT-\u003e {\"level\":3,\"time\":...,\"reqId\":1,\"foo\":\"bar\",\"aaa\":1,\"msg\":\"hello again\"}\n\nvar log3 = log2.child({ baz: \"333\" });\nlog3.info(\"yet again\");\n// OUT-\u003e {\"level\":3,\"time\":...,\"reqId\":1,\"foo\":\"bar\",\"baz\":\"333\",\"msg\":\"yet again\"}\n```\n\nYou can override a parent data context key, but it will write both keys to the log so no data is lost.\n\n```js\nvar log2 = log.child({ aaa: \"base\" });\nvar log3 = log2.child({ aaa: \"over1\" });\nlog3.info(\"try to overwrite\", { aaa: \"inline\" });\n// OUT-\u003e {\"level\":3,\"time\":...,\"aaa\":\"base\",\"aaa\":\"over1\",\"aaa\":\"inline\",\"msg\":\"try to overwrite\"}\n```\n\n## Customize\n\nThis library exports the tools you need to build your own custom logger settings.\n\n```js\nimport { toJson, stringifyPairs, timeFns, mkLevel } from \"json-log\";\n```\n\n### str = toJson(data)\n\nJson stringify anything in a safe way that makes sense for logging.\n\n### str = stringifyPairs(data)\n\nSame as toJson except ment to be a fragment worked into a larger json line.\n\n```js\n'\"foo\":\"bar\",' === stringifyPairs({ foo: \"bar\" });\n```\n\n### timeFns\n\n```js\nexport const timeFns = {\n  iso: () =\u003e `\"time\":\"${new Date().toISOString()}\",`,\n  now: () =\u003e `\"time\":${Date.now()},`,\n  none: () =\u003e \"\"\n};\n```\n\n### fn = mkLevel(level, time, ctx, write)\n\nThis returns the logger function for a given level. See below for how it's used.\n\n```js\nclass MyAwesomeLogger {\n  constructor(ctx) {\n    this.ctx = ctx;\n\n    // MyAwesomeLogger is soo much better than the default \"json-log\"\n    // Why may you ask? Because 3 log levels are never enough!\n\n    this.emergency = mkLevel(0, timeFns.iso, ctx, writeStdErr);\n    this.panic = mkLevel(10, timeFns.iso, ctx, writeStdErr);\n    this.alert = mkLevel(20, timeFns.iso, ctx, writeStdErr);\n    this.critical = mkLevel(30, timeFns.iso, ctx, writeStdErr);\n    this.error = mkLevel(100, timeFns.iso, ctx, writeStdErr);\n    this.danger = mkLevel(110, timeFns.iso, ctx, writeStdErr);\n    this.warn = mkLevel(200, timeFns.iso, ctx, writeStdOut);\n    this.info = mkLevel(300, timeFns.iso, ctx, writeStdOut);\n    this.debug = mkLevel(310, timeFns.now, ctx, writeStdOut);\n    this.trace = mkLevel(400, () =\u003e `\"t\":${Date.now},`, ctx, writeStdOut);\n    this.blackhole = mkLevel(666, timeFns.none, ctx, function(line) {});\n  }\n  child(moreCtx) {\n    return new MyAwesomeLogger(this.ctx + stringifyPairs(moreCtx));\n  }\n}\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmallhelm%2Fjson-log","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmallhelm%2Fjson-log","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmallhelm%2Fjson-log/lists"}