{"id":20253546,"url":"https://github.com/graphile/logger","last_synced_at":"2025-04-10T23:43:23.544Z","repository":{"id":43136678,"uuid":"358571377","full_name":"graphile/logger","owner":"graphile","description":"An extremely lightweight log abstraction for libraries that can be used in place of `console.log`/etc, allowing users to optionally log through other log providers (`winston`, `bunyan`, etc)","archived":false,"fork":false,"pushed_at":"2022-04-20T15:15:00.000Z","size":98,"stargazers_count":9,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-10T23:43:15.152Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","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/graphile.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-04-16T11:05:59.000Z","updated_at":"2024-11-19T00:32:31.000Z","dependencies_parsed_at":"2022-08-22T10:40:33.078Z","dependency_job_id":null,"html_url":"https://github.com/graphile/logger","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphile%2Flogger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphile%2Flogger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphile%2Flogger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphile%2Flogger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/graphile","download_url":"https://codeload.github.com/graphile/logger/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248317726,"owners_count":21083527,"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":[],"created_at":"2024-11-14T10:25:33.758Z","updated_at":"2025-04-10T23:43:23.524Z","avatar_url":"https://github.com/graphile.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @graphile/logger\n\nThere's a lot of logging frameworks out there; if we picked one then it'd be the\nwrong one for many users. Just using `console.log` isn't optimal for people who\nwant a consistent logging solution.\n\n`@graphile/logger` acts as an extremely lightweight zero-dependency\nTypeScript-native abstraction; it allows our libraries to get on with the job of\nlogging whilst allowing library users to override how/where the logs are output.\n\nOriginally we\n[built this for Graphile Worker](https://github.com/graphile/worker/blob/main/src/logger.ts),\nbut as desire for similar facilities in our other projects grew we decided to\nroll it out into its own project.\n\n## Status\n\nFeature complete. Ignoring comments, once compiled it's only about 60 lines of\ncode!\n\n(This is a Node library, it's not intended to be used in the browser.)\n\n## Installation\n\nInstall via `yarn add @graphile/logger` or `npm install @graphile/logger`.\n\n## Usage for library consumers\n\nThis section is for users of a library that users `@graphile/logger` who want to\nknow how to hook up their preferred logging software instead of using the\n`console` fallback. If you're a library author and you want to use\n`@graphile/logger` in your project, please instead see\n[Usage for library authors](#usage-for-library-authors) below.\n\n### Creating a custom Logger\n\nTo create a custom logger you first need to create a _log function factory_ -\nthat is to say a function which returns a _log function_. Here's a trivially\nsimple _log function factory_ which logs to `console`:\n\n```js\nfunction logFunctionFactory(scope) {\n  return function logFunction(level, message, meta) {\n    console.log(`${level}: ${message}`);\n  }\n}\n```\n\nYour _log function factory_ should conform to the `LogFunctionFactory` interface\ndefined in the source code.\n\nOnce you have your _log function factory_ you can create a logger from it:\n\n```ts\nimport { Logger } from \"@graphile/logger\";\n\nconst logger = new Logger(logFunctionFactory);\n```\n\nNow you can pass your custom `logger` to the library you're using.\n\n### Bunyan example\n\nHere's an example of logging with\n[bunyan](https://github.com/trentm/node-bunyan):\n\n```ts\nimport { Logger } from \"@graphile/logger\";\nimport bunyan from \"bunyan\";\n\nconst bunyanLog = bunyan.createLogger({ name: \"myapp\" });\n\nconst logger = new Logger((scope) =\u003e {\n  const scopedBunyanLog = bunyanLog.child(scope);\n  return (level, message, meta) =\u003e {\n    switch (level) {\n      case \"error\":\n        return scopedBunyanLog.error(`%s`, message, meta);\n      case \"warning\":\n        return scopedBunyanLog.warn(`%s`, message, meta);\n      case \"debug\":\n        return scopedBunyanLog.debug(`%s`, message, meta);\n      case \"info\":\n      default:\n        return scopedBunyanLog.info(`%s`, message, meta);\n    }\n  };\n});\n\nlogger.info(\"Hello with Bunyan\", { randomNumber: { fairDiceRoll: 4 } });\n```\n\n## Usage for library authors\n\nThis section is for library authors who want to use `@graphile/logger` in their\nlibrary. If you're a user of a library that users `@graphile/logger` and you\nwant to know how to hook up your preferred logging software, please see\n[Usage for library consumers](#usage-for-library-consumers) above.\n\n### Logger\n\nWhen you're logging in your library code, you'll be dealing with a `Logger`\ninstance (for creating your own `Logger` instance, see\n[Usage for library consumers](#usage-for-library-consumers) above). `Logger` has\n5 methods you'll care about.\n\nThe first for methods ─ `error`, `warn`, `info` and `debug` ─ are your four\nlogging methods. They all accept a message string and an optional meta object.\nIt's often useful to put additional information into the meta object so that\npeople who are doing structured (i.e. JSON) logging can filter on these special\nproperties. We strongly recommend you only put JSON-able values into the meta,\nand be careful with personally identifiable information (PII) just as you would\nwith the log message itself.\n\n```ts\nlogger.info(\"Hello world!\");\nlogger.error(\"++?????++ Out of Cheese Error. Redo From Start.\");\nlogger.debug(\"The answer to the big question... 42\", { meaningOfLife: 42 });\n```\n\nThe fifth method, `scope`, returns a new `Logger` instance with a modified\n(generally narrower) scope.\n\n```ts\nconst newLogger = logger.scope({ id: request.id });\n```\n\nYou can use the scope to give more \"ambient\" information to your users, telling\nthem about the context in which the logger is running - e.g. what's the task or\nrequest identifier that's being dealt with.\n\n### `defaultLogger`\n\nGreat as a fallback when the user opts to not pass their own custom logger, this\nrequires no setup and just logs to `console`; you should use this as the\nfallback if you don't have specific logging requirements.\n\nExample:\n\n```ts\nimport { defaultLogger } from \"@graphile/logger\";\n\nfunction myAwesomeLibraryMethod(logger = defaultLogger) {\n  logger.info(\"Hi\");\n  logger.info(\"Hi with meta\", { meta: true, meaning: 42 });\n}\n\nmyAwesomeLibraryMethod();\n```\n\n### `makeCustomLogFactory`\n\nIf you want your logs to be a little more custom, or there's particular\ninformation you want them to include from the scope, then a logger based on a\ncustom log factory is probably what you want to use as the fallback in your\nlibrary, rather than `defaultLogger`. For example, in Graphile Worker we have a\ndefault logger that factors the `workerId`, `taskIdentifier` and `jobId` into\nthe output log messages.\n\nYou can read more about this in the source of this library (which is short and\nheavily documented); here's an illustrative example:\n\n```ts\nimport { Logger, makeConsoleLogFactory } from \"@graphile/logger\";\n\ninterface LogScope {\n  label?: string;\n  workerId?: string;\n  taskIdentifier?: string;\n  jobId?: string;\n}\n\nconst graphileWorkerDefaultLogger = new Logger\u003cLogScope\u003e(\n  makeConsoleLogFactory({\n    format: `[%s%s] %s: %s`,\n    formatParameters(level, message, scope) {\n      const taskText = scope.taskIdentifier ? `: ${scope.taskIdentifier}` : \"\";\n      const jobIdText = scope.jobId ? `{${scope.jobId}}` : \"\";\n      return [\n        scope.label || \"core\",\n        scope.workerId ? `(${scope.workerId}${taskText}${jobIdText})` : \"\",\n        level.toUpperCase(),\n        message,\n      ];\n    },\n  }),\n);\n\nfunction worker(logger: Logger\u003cLogScope\u003e = graphileWorkerDefaultLogger) {\n  logger.info(\"Starting worker cluster...\");\n\n  // ...\n\n  const workerLogger = logger.scope({\n    workerId: \"3b0e05e1-beed-48a6-9d2b-055e47a29b5f\",\n  });\n  workerLogger.info(\"Looking for jobs...\");\n\n  // ...\n\n  const jobLogger = workerLogger.scope({\n    taskIdentifier: \"my_task\",\n    jobId: 84,\n  });\n  jobLogger.info(\"Starting job...\");\n}\n\nworker();\n```\n\nwhich would output:\n\n```\n[core] INFO: Starting worker cluster...\n[core(3b0e05e1-beed-48a6-9d2b-055e47a29b5f)] INFO: Looking for jobs...\n[core(3b0e05e1-beed-48a6-9d2b-055e47a29b5f: my_task{84})] INFO: Starting job...\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgraphile%2Flogger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgraphile%2Flogger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgraphile%2Flogger/lists"}