{"id":15066010,"url":"https://github.com/mekh/logis","last_synced_at":"2025-04-10T13:40:35.991Z","repository":{"id":58082549,"uuid":"301047955","full_name":"mekh/logis","owner":"mekh","description":"A simple and lightweight logger for Node.JS","archived":false,"fork":false,"pushed_at":"2022-09-24T17:14:53.000Z","size":264,"stargazers_count":4,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-29T21:05:17.014Z","etag":null,"topics":["console","debug","error","info","log","logger","logging","logs","nodejs","trace","warn"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mekh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-10-04T05:37:50.000Z","updated_at":"2024-11-18T09:11:49.000Z","dependencies_parsed_at":"2023-01-18T21:32:32.784Z","dependency_job_id":null,"html_url":"https://github.com/mekh/logis","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mekh%2Flogis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mekh%2Flogis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mekh%2Flogis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mekh%2Flogis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mekh","download_url":"https://codeload.github.com/mekh/logis/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248055284,"owners_count":21040157,"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","debug","error","info","log","logger","logging","logs","nodejs","trace","warn"],"created_at":"2024-09-25T00:59:45.336Z","updated_at":"2025-04-10T13:40:35.972Z","avatar_url":"https://github.com/mekh.png","language":"JavaScript","readme":"# loggis [![Tests](https://github.com/mekh/logis/actions/workflows/test.yml/badge.svg)](https://github.com/mekh/logis/actions/workflows/test.yml) [![Coverage Status](https://coveralls.io/repos/github/mekh/logis/badge.svg?branch=main)](https://coveralls.io/github/mekh/logis?branch=main)\nA simple, lightweight,   console logger for Node.JS.\n\nA perfect choice for Kubernetes, Docker, and other systems that collect logs directly from stdout.\n\n# Features\n- no dependencies\n- [ready for usage right out of the box](#quick-start)\n- [supports JSON](#json-format)\n- [global and individual configuration of loggers](#the-default-and-an-individual-configuration)\n- [automatic objects serialization](#quick-start)\n- [automatic circular structures handling](#circulars)\n- [robust configuration](#advance-configuration)\n- [tracing information - caller's file and function name, and even line number where the log method has been called](#quick-start)\n- [colored output](#configuration)\n- safe for any kind of data - Express req/res, Sequelize models, Error objects, etc.\n- both **CommonJS** and [**ESM**](#esm) are supported\n- **Typescript** friendly\n\n# Installation\n```bash\n$ npm install loggis\n```\n\n# Quick start\nThe logger can be used right out of the box, i.e. it does not require any configuration by default.\nThe default settings are:\n- loglevel: info\n- colorize: false\n- [logline](#logline): [ISO timestamp] [level] [process.pid] [category] [filename||functionName:lineNumber] message\n- [primitives](#primitives-default-configuration)\n  - Function =\u003e \u003cFunction ${function.name || 'anonymous'}\u003e\n  - Date instance =\u003e Date.toISOString()\n  - Promise instance =\u003e '\u003cPromise\u003e'\n  - Buffer instance - Buffer.toString()\n  - Error instance =\u003e error properties/values concatenated by a new line\n\n```js\n// /app/test_log.js\nconst logger = require('loggis');\n\nconst logFn = () =\u003e {\n  logger.info('its simple');\n  logger.error('this', ['will', 'be'], { serialized: { into: 'a string' } });\n\n  // won't be printed since the default loglevel is info\n  logger.trace('trace info');\n};\n\nlogFn();\n// [2022-09-03T08:17:26.549Z] [INFO] [123] [default] [/app/test_log.js||logFn:4] its simple\n// [2022-09-03T08:17:26.554Z] [ERROR] [123] [default] [/app/test_log.js||logFn:5] this [\"will\",\"be\"] {\"serialized\":{\"into\":\"a string\"}}\n\n```\n\n# Configuration\nThe default configuration can be set either through the `configure` method or the environment variables.\nEach logger instance can be configured individually by setting an instance properties like `level`, `colorize`, etc.\n\n## Environment variables\nThe default configuration can be set through the environment variables.\n\n| Name       | Type    | Default value | Description                       |\n|------------|---------|---------------|-----------------------------------|\n| LOG_LEVEL  | String  | info          | The default logging level         |\n| LOG_JSON   | Boolean | false         | Turns on/off JSON output          |\n| LOG_COLORS | Boolean | false         | Turns on/off the colorized output |\n\n## Configure options\nThe default configuration can be passed to the `configure` method.\nIt accepts the following parameters:\n- loglevel - the default logging level, valid values are `error`, `warn`, `info`, `debug`, and `trace`\n- json - use json output\n- colorize - use colored output\n- [format](#custom-formatter-deprecated) - custom message formatter (DEPRECATED)\n- [logline](#logline) - the Logline instance\n- [primitives](#primitives) - the Primitives instance\n\nThe same parameters can be used for an individual configuration of a logger.\n\n## Circulars\nIt's safe to pass any kind of arguments to the log functions, including Express requests, Sequelize models, etc.\nThe logger automatically detects circular structures and replaces them by string references.\nThe reference is a path within an object.\n\n```js\nconst logger = require('loggis');\n\nconst a = { a: 1 };\na.b = a; // circular reference\na.c = [1, { d: 2, e: { f: 'abc' } }]\na.g = a.c[1].e; // circular reference\n\nlogger.info(a);\n// ...{\"a\":1,\"b\":\"[REF =\u003e .]\",\"c\":[1,{\"d\":2,\"e\":{\"f\":\"abc\"}}],\"g\":\"[REF =\u003e c[1].e]\"}\n```\n\n## Custom formatter (deprecated)\nThe formatter function accepts an object with the following properties:\n- args -  an array of arguments that was passed to a log method\n- level - logging level\n- logger - logger instance\n\nIt might be convenient to set the default message format for a particular application.\n  \nFor example, if you want to:\n- log message in JSON format\n- define and log the module name from which the log method is called\n- set your own dataset that should be logged\n- etc\n\nYou can do it like this:\n```js\n// ./src/logger/index.js\nconst loggis = require('loggis');\n\nconst customFormatter = ({ args, level, logger }) =\u003e JSON.stringify({\n  date: new Date(),\n  module: module.parent.filename.replace(process.cwd(), ''),\n  category: logger.category,\n  level,\n  data: args, // be careful, it might crash if data is not compatible with JSON.stringify\n});\n\nloggis.configure({ format: customFormatter });\n\nmodule.exports = loggis;\n\n// ./src/app.js\nconst logger = require('./logger');\n\nconst log = logger.getLogger('MY_APP');\n\nlog.info('the configuration is =\u003e', {\n  level: logger.loglevel,\n  color: logger.colorize,\n  json: logger.json,\n});\n\n// ./index.js\nrequire('./src/app');\n\n// {\"date\":\"2022-09-03T08:17:26.549Z\",\"module\":\"/app.js\",\"category\":\"MY_APP\",\"level\":\"info\",\"data\":[\"the configuration is =\u003e\",{\"level\":\"info\",\"color\":false,\"json\":false}]}\n````\n\n# Usage examples\n### Set the default configuration, get a logger, use it\n```js\nconst logger = require('loggis')\n\nconst log = logger.configure({ loglevel: 'debug' }).getLogger('MY_APP');\n\nlog.error('easy to log error')\nlog.debug('easy to log debug');\nlog.trace('will not be printed, since the log level is DEBUG');\n// [2022-09-03T08:17:26.549Z] [ERROR] [123] [MY_APP] [/app/test_log.js||-:5] easy to log error\n// [2022-09-03T08:17:26.554Z] [DEBUG] [123] [MY_APP] [/app/test_log.js||-:6] easy to log debug\n\n```\n\n### JSON format\n```js\nconst logger = require('loggis');\n\nlogger.configure({ json: true });\n\nlogger.info('user info =\u003e', { id: 1, name: 'John', email: 'mail@g.co' });\n\n// {\"date\":\"2022-09-03T08:17:26.549Z\",\"level\":\"info\",\"pid\":123,\"category\":\"json\",\"filename\":\"/app/test_log.js\",\"function\":\"-\",\"line\":5,\"data\":[\"user info =\u003e\",{\"id\":1,\"name\":\"John\",\"email\":\"mail@g.co\"}]}\n\n```\n\n### The default and an individual configuration\n```js\nconst logger = require('loggis');\n\nlogger.configure({ loglevel: 'warn', colorize: true });\n\nconst logLine = logger.getLogger('line'); // the default configuration will be applied\nconst logJson = logger.getLogger('json');\n\n// configure an instance\nlogJson.loglevel = 'trace';\nlogJson.json = true;\nlogJson.colorize = false;\n\nlogLine.info('the configuration is =\u003e', {\n  level: logLine.loglevel,\n  color: logLine.colorize,\n  json: logLine.json,\n});\n\nlogJson.trace('the configuration is =\u003e', {\n  level: logJson.loglevel,\n  color: logJson.colorize,\n  json: logJson.json,\n});\n\n\n// [2022-09-03T08:17:26.549Z] [WARN] [123] [line] [/app/test_log.js||-:13] the configuration is =\u003e {\"level\":\"warn\",\"color\":true,\"json\":false}\n// {\"date\":\"2022-09-03T08:17:26.554Z\",\"level\":\"trace\",\"pid\":123,\"category\":\"json\",\"filename\":\"/app/test_log.js\",\"function\":\"-\",\"line\":19,\"data\":[\"the configuration is =\u003e\",{\"level\":\"trace\",\"color\":false,\"json\":true}]}\n````\n\n## ESM\n```ejs\nimport logger from 'loggis';\n\nconst log = logger.getLogger('MY_APP');\n```\n```ejs\nimport { getLogger } from 'loggis';\n\nconst log = getLogger('MY_APP')\n````\n\n# Advance configuration\n## Primitives\nData processing includes two stages:\n- parsing each argument passed to the logger function\n- formatting the string that will be printed\n\nThe parsing of each argument looks like this:\n- the parser checks if the formatting rules are defined for the given element\n- if such rules were found, they are sequentially applied to the element\n- if the element is an object, then for each nested element (array item, object key value), the entire chain is repeated\n\nThis way it's possible to format any element at any level of nesting.\n\nAn element for which one or more formatting rules are specified is called a `primitive`.\n\nThe formatting rules for primitives are set by an instance of the `Primitives` class, the `add` method.\nThis method takes two arguments:\n- the first one is a function that takes an element as an argument and returns a boolean if the element is the given primitive\n- the second one is a function that takes an element as an argument and returns the modified element\n\nThe `add` method returns the instance itself, so the method can be chained. \n\nThe `Primitives` class is available in the `formatters` property of the logger.\n\nAn instance of the `Primitives` class can be passed to the `configure` method of the logger as the `primitives` parameter.\n\nExample:\n```js\nconst logger = require('loggis');\n\nconst primitives = new logger.formatters.Primitives()\n  .add(\n    (item) =\u003e typeof item === 'number', // for any number\n    (item) =\u003e item.toFixed(2),          // apply this function\n  );\n\nlogger.configure({ primitives });\n\nlogger.info(10.987654, '123.1589', { float: 1.5499, int: 1, str: '9.98765' });\n// ... 10.99 123.1589 {\"float\":\"1.55\",\"int\":\"1.00\",\"str\":\"9.98765\"}\n```\n\nFor convenience, the `Primitives` class has two static methods - `typeof` and `instanceof` with the following definitions:\n```typescript\n  interface Cls\u003cT, A extends any[] = any[]\u003e extends Function { new(...args: A): T; }\n  \n  static typeof\u003cT = any\u003e(type: string): ((data: T) =\u003e boolean);\n  static instanceof\u003cT, V = any\u003e(cls: Cls\u003cT\u003e): ((data: V) =\u003e boolean);\n```\n\n#### Primitives default configuration\n```js\nprimitives\n  .add(Primitives.typeof('function'),  (data) =\u003e `\u003cFunction ${data.name || 'anonymous'}\u003e`)\n  .add(Primitives.instanceof(Date),    (date) =\u003e date.toISOString())\n  .add(Primitives.instanceof(Buffer),  (data) =\u003e data.toString())\n  .add(Primitives.instanceof(Promise), () =\u003e '\u003cPromise\u003e')\n  .add(Primitives.instanceof(Error),   (error) =\u003e Object\n    .getOwnPropertyNames(error)\n    .reduce((acc, prop) =\u003e `${acc}\\n${prop}: ${error[prop]}`, ''));\n\n```\n\n### Primitives usage examples\n#### Filter sensitive data:\n```js\nconst logger = require('loggis');\nconst { Primitives } = logger.formatters;\n\nconst isObject = (obj) =\u003e typeof obj === 'object' \u0026\u0026 obj !== null \u0026\u0026 !Array.isArray(obj);\nconst hide = (obj, prop) =\u003e (Object.hasOwn(obj, prop) ? ({ ...obj, [prop]: '***' }) : obj);\n\nconst primitives = new Primitives()\n  .add(isObject, (obj) =\u003e hide(obj, 'password'))\n  .add(isObject, (obj) =\u003e hide(obj, 'card_number'))\n  .add(isObject, (obj) =\u003e hide(obj, 'card_cvv'));\n\nlogger.configure({ primitives });\n\nlogger.info({ user: { id: 1, name: 'John', password: 'secret', card: { card_cvv: 321, card_number: 4111111111111111 } } });\n// ... {\"user\":{\"id\":1,\"name\":\"John\",\"password\":\"***\",\"card\":{\"card_cvv\":\"***\",\"card_number\":\"***\"}}}\n```\n\n#### Sequelize models serialization\n```js\nconst { Model } = require('sequelize');\nconst logger = require('loggis');\n\nconst primitives = new logger.formatters.Primitives()\n  .add(Primitives.instanceof(Model), (model) =\u003e model.toJSON())\n```\n\n## Logline\nThe elements to be printed are specified by calling the `add` method of an instance of the `Logline` class.\n\nThe `add` method accepts a `Message` instance and returns any type of data.\n\nThe `Message` instance has the following properties:\n- date - new Date()\n- pid - process.pid\n- data - an array of items returned by the Parser (see [Primitives](#primitives))\n- level - message level, i.e. for log.error it will be error\n- category - logger category (logger.getLogger('myapp') =\u003e the category is myapp);\n- fileName - the name of the file where the logger function was called\n- lineNumber - line number where the logger method was called\n- functionName - name of the function from which the logger method was called\n- text - could be a string or an array depending on `json` option of the logger\n\nThe `Logline` class is available in the `formatters` property of the logger.\n\nThe `join` method of the logline instance defines a separator that is used while joining all the logline elements. It does not work for JSON format.\n\n```js\nconst logger = require('loggis');\nconst { Logline } = logger.formatters;\n\n// --- Simplest format ---\nconst logline = new Logline().add(message =\u003e message.text);\nlogger.configure({ logline }).info(1, [2, 3], { 4: 5 }); // 1 [2,3] {\"4\":5}\n\n// --- Static text ---\nconst logline = new Logline()\n        .add(() =\u003e '[static_text]')\n        .add(message =\u003e `[${message.text}]`);\nlogger.configure({ logline }).info('log message'); // [static_text] [log message]\n\n// --- JOIN ---\nconst logline = new Logline()\n        .add(message =\u003e message.date.valueOf())\n        .add(message =\u003e message.level.toUpperCase())\n        .add(message =\u003e message.pid)\n        .add(message =\u003e message.text)\n        .join(' | ');\nlogger.configure({ logline }).info('log message'); // 1662193046549 | INFO | 123 | log message\n\n// --- JSON format ---\nconst logline = new Logline()\n        .add(message =\u003e ({ date: message.date }))\n        .add(message =\u003e ({ message: message.text }));\nlogger.configure({ json: true, logline }).info('user =\u003e', { id: 1, name: 'John' }); // {\"date\":\"2022-09-03T08:17:26.549Z\",\"message\":[\"user =\u003e\",{\"id\":1,\"name\":\"John\"}]}\n```\n\n#### Default logline\n```js\nconst wrap = data =\u003e `[${data}]`;\n\nlogline\n  .add(message =\u003e wrap(message.date.toISOString()))\n  .add(message =\u003e wrap(message.level.toUpperCase()))\n  .add(message =\u003e wrap(message.pid))\n  .add(message =\u003e wrap(message.category))\n  .add(message =\u003e wrap(`${message.fileName}||${message.functionName || '-'}:${message.lineNumber || -1}`))\n  .add(message =\u003e message.text);\n\n```\n#### Default json logline\n```js\nloglineJson\n  .add(message =\u003e ({ date: message.date.toISOString() }))\n  .add(message =\u003e ({ level: message.level }))\n  .add(message =\u003e ({ pid: message.pid }))\n  .add(message =\u003e ({ category: message.category }))\n  .add(message =\u003e ({ filename: message.fileName }))\n  .add(message =\u003e ({ function: message.functionName || '-' }))\n  .add(message =\u003e ({ line: message.lineNumber || -1 }))\n  .add(message =\u003e ({ data: message.text }));\n```\n\n## License\n[MIT](https://opensource.org/licenses/MIT \"The MIT License\")\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmekh%2Flogis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmekh%2Flogis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmekh%2Flogis/lists"}