diff --git a/.release-please-manifest.json b/.release-please-manifest.json index dbc9e2fa5a3..82085ef6ea9 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,5 @@ { + "handwritten/nodejs-logging-winston": "6.0.1", "packages/gapic-node-processing": "0.1.6", "packages/google-ads-admanager": "0.5.0", "packages/google-ads-datamanager": "0.1.0", @@ -145,11 +146,11 @@ "packages/google-cloud-saasplatform-saasservicemgmt": "0.1.1", "packages/google-cloud-scheduler": "5.3.1", "packages/google-cloud-secretmanager": "6.1.1", + "packages/google-cloud-securesourcemanager": "0.8.1", "packages/google-cloud-security-privateca": "7.0.1", "packages/google-cloud-security-publicca": "2.2.1", "packages/google-cloud-securitycenter": "9.2.1", "packages/google-cloud-securitycentermanagement": "0.7.1", - "packages/google-cloud-securesourcemanager": "0.8.1", "packages/google-cloud-servicedirectory": "6.1.1", "packages/google-cloud-servicehealth": "0.7.1", "packages/google-cloud-shell": "4.1.1", @@ -219,4 +220,4 @@ "packages/typeless-sample-bot": "3.1.1", "packages/google-cloud-apiregistry": "0.1.0", "packages/google-cloud-gkerecommender": "0.1.0" -} \ No newline at end of file +} diff --git a/handwritten/nodejs-logging-winston/.OwlBot.yaml b/handwritten/nodejs-logging-winston/.OwlBot.yaml new file mode 100644 index 00000000000..10389756341 --- /dev/null +++ b/handwritten/nodejs-logging-winston/.OwlBot.yaml @@ -0,0 +1,17 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +begin-after-commit-hash: 674a41e0de2869f44f45eb7b1a605852a5394bba + diff --git a/handwritten/nodejs-logging-winston/.compodocrc b/handwritten/nodejs-logging-winston/.compodocrc new file mode 100644 index 00000000000..cd8b42152a6 --- /dev/null +++ b/handwritten/nodejs-logging-winston/.compodocrc @@ -0,0 +1,10 @@ +--- +tsconfig: ./tsconfig.json +output: ./docs +theme: material +hideGenerator: true +disablePrivate: true +disableProtected: true +disableInternal: true +disableCoverage: true +disableGraph: true diff --git a/handwritten/nodejs-logging-winston/.eslintignore b/handwritten/nodejs-logging-winston/.eslintignore new file mode 100644 index 00000000000..c4a0963e9bd --- /dev/null +++ b/handwritten/nodejs-logging-winston/.eslintignore @@ -0,0 +1,8 @@ +**/node_modules +**/coverage +test/fixtures +build/ +docs/ +protos/ +samples/generated/ +system-test/**/fixtures diff --git a/handwritten/nodejs-logging-winston/.eslintrc.json b/handwritten/nodejs-logging-winston/.eslintrc.json new file mode 100644 index 00000000000..78215349546 --- /dev/null +++ b/handwritten/nodejs-logging-winston/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/gts" +} diff --git a/handwritten/nodejs-logging-winston/.gitattributes b/handwritten/nodejs-logging-winston/.gitattributes new file mode 100644 index 00000000000..33739cb74e4 --- /dev/null +++ b/handwritten/nodejs-logging-winston/.gitattributes @@ -0,0 +1,4 @@ +*.ts text eol=lf +*.js text eol=lf +protos/* linguist-generated +**/api-extractor.json linguist-language=JSON-with-Comments diff --git a/handwritten/nodejs-logging-winston/.gitignore b/handwritten/nodejs-logging-winston/.gitignore new file mode 100644 index 00000000000..1d9188611b5 --- /dev/null +++ b/handwritten/nodejs-logging-winston/.gitignore @@ -0,0 +1,17 @@ +**/*.log +**/node_modules +.coverage +.nyc_output +.vscode +docs/ +out/ +build/ +system-test/secrets.js +system-test/*key.json +*.lock +.DS_Store +google-cloud-logging-winston-*.tgz +google-cloud-logging-bunyan-*.tgz +key.json +__pycache__ +package-lock.json diff --git a/handwritten/nodejs-logging-winston/.jsdoc.js b/handwritten/nodejs-logging-winston/.jsdoc.js new file mode 100644 index 00000000000..946389a1b88 --- /dev/null +++ b/handwritten/nodejs-logging-winston/.jsdoc.js @@ -0,0 +1,51 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +'use strict'; + +module.exports = { + opts: { + readme: './README.md', + package: './package.json', + template: './node_modules/jsdoc-fresh', + recurse: true, + verbose: true, + destination: './docs/' + }, + plugins: [ + 'plugins/markdown', + 'jsdoc-region-tag' + ], + source: { + excludePattern: '(^|\\/|\\\\)[._]', + include: [ + 'build/src' + ], + includePattern: '\\.js$' + }, + templates: { + copyright: 'Copyright 2026 Google, LLC.', + includeDate: false, + sourceFiles: false, + systemName: '@google-cloud/logging-winston', + theme: 'lumen', + default: { + "outputSourceFiles": false + } + }, + markdown: { + idInHeadings: true + } +}; diff --git a/handwritten/nodejs-logging-winston/.mocharc.js b/handwritten/nodejs-logging-winston/.mocharc.js new file mode 100644 index 00000000000..2431859019f --- /dev/null +++ b/handwritten/nodejs-logging-winston/.mocharc.js @@ -0,0 +1,29 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +const config = { + "enable-source-maps": true, + "throw-deprecation": true, + "timeout": 10000, + "recursive": true +} +if (process.env.MOCHA_THROW_DEPRECATION === 'false') { + delete config['throw-deprecation']; +} +if (process.env.MOCHA_REPORTER) { + config.reporter = process.env.MOCHA_REPORTER; +} +if (process.env.MOCHA_REPORTER_OUTPUT) { + config['reporter-option'] = `output=${process.env.MOCHA_REPORTER_OUTPUT}`; +} +module.exports = config diff --git a/handwritten/nodejs-logging-winston/.nycrc b/handwritten/nodejs-logging-winston/.nycrc new file mode 100644 index 00000000000..b18d5472b62 --- /dev/null +++ b/handwritten/nodejs-logging-winston/.nycrc @@ -0,0 +1,24 @@ +{ + "report-dir": "./.coverage", + "reporter": ["text", "lcov"], + "exclude": [ + "**/*-test", + "**/.coverage", + "**/apis", + "**/benchmark", + "**/conformance", + "**/docs", + "**/samples", + "**/scripts", + "**/protos", + "**/test", + "**/*.d.ts", + ".jsdoc.js", + "**/.jsdoc.js", + "karma.conf.js", + "webpack-tests.config.js", + "webpack.config.js" + ], + "exclude-after-remap": false, + "all": true +} diff --git a/handwritten/nodejs-logging-winston/.prettierignore b/handwritten/nodejs-logging-winston/.prettierignore new file mode 100644 index 00000000000..9340ad9b86d --- /dev/null +++ b/handwritten/nodejs-logging-winston/.prettierignore @@ -0,0 +1,6 @@ +**/node_modules +**/coverage +test/fixtures +build/ +docs/ +protos/ diff --git a/handwritten/nodejs-logging-winston/.prettierrc.js b/handwritten/nodejs-logging-winston/.prettierrc.js new file mode 100644 index 00000000000..d2eddc2ed89 --- /dev/null +++ b/handwritten/nodejs-logging-winston/.prettierrc.js @@ -0,0 +1,17 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +module.exports = { + ...require('gts/.prettierrc.json') +} diff --git a/handwritten/nodejs-logging-winston/.readme-partials.yaml b/handwritten/nodejs-logging-winston/.readme-partials.yaml new file mode 100644 index 00000000000..f8194babf4b --- /dev/null +++ b/handwritten/nodejs-logging-winston/.readme-partials.yaml @@ -0,0 +1,249 @@ +introduction: |- + This module provides a higher-level layer for working with + [Cloud Logging](https://cloud.google.com/logging/docs), compatible with + [Winston](https://www.npmjs.com/package/winston). Simply attach this as a + transport to your existing Winston loggers. + +body: |- + For a more detailed Cloud Logging setup guide, see https://cloud.google.com/logging/docs/setup/nodejs. + + Creates a Winston logger that streams to Cloud Logging + + Logs will be written to: "projects/YOUR_PROJECT_ID/logs/winston_log" + + ### Using as an express middleware + + ***NOTE: this feature is experimental. The API may change in a backwards + incompatible way until this is deemed stable. Please provide us feedback so + that we can better refine this express integration.*** + + We provide a middleware that can be used in an express application. Apart from + being easy to use, this enables some more powerful features of Cloud + Logging: request bundling. Any application logs emitted on behalf of a specific + request will be shown nested inside the request log as you see in this + screenshot: + + ![Request Bundling Example](https://raw.githubusercontent.com/googleapis/nodejs-logging-winston/master/doc/images/request-bundling.png) + + This middleware adds a `winston`-style log function to the `request` object. + You can use this wherever you have access to the `request` object (`req` in the + sample below). All log entries that are made on behalf of a specific request are + shown bundled together in the Cloud Logging UI. + + ```javascript + const lw = require('@google-cloud/logging-winston'); + const winston = require('winston'); + + // Import express module and create an http server. + const express = require('express'); + const logger = winston.createLogger(); + + async function main() { + // Create a middleware that will use the provided logger. + // A Cloud Logging transport will be created automatically + // and added onto the provided logger. + const mw = await lw.express.makeMiddleware(logger); + // Alternatively, you can construct a LoggingWinston transport + // yourself and pass it int. + // const transport = new LoggingWinston({...}); + // const mw = await lw.express.makeMiddleware(logger, transport); + + const app = express(); + + // Install the logging middleware. This ensures that a Winston-style `log` + // function is available on the `request` object. Attach this as one of the + // earliest middleware to make sure that the log function is available in all + // subsequent middleware and routes. + app.use(mw); + + // Setup an http route and a route handler. + app.get('/', (req, res) => { + // `req.log` can be used as a winston style log method. All logs generated + // using `req.log` use the current request context. That is, all logs + // corresponding to a specific request will be bundled in the Cloud Logging + // UI. + req.log.info('this is an info log message'); + res.send('hello world'); + }); + + // `logger` can be used as a global logger, one not correlated to any specific + // request. + logger.info('bonjour'); + + // Start listening on the http server. + app.listen(8080, () => { + logger.info('http server listening on port 8080'); + }); + } + + main(); + ``` + + ### Error Reporting + + Any `Error` objects you log at severity `error` or higher can automatically be picked up by [Error Reporting](https://cloud.google.com/error-reporting/) if you have specified a `serviceContext.service` when instantiating a `LoggingWinston` instance: + + ```javascript + const loggingWinston = new LoggingWinston({ + serviceContext: { + service: 'my-service', // required to report logged errors + // to the Error Reporting + // console + version: 'my-version' + } + }); + ``` + + It is an error to specify a `serviceContext` but not specify `serviceContext.service`. + + Make sure to add logs to your [uncaught exception](https://nodejs.org/api/process.html#process_event_uncaughtexception) and [unhandled rejection](https://nodejs.org/api/process.html#process_event_unhandledrejection) handlers if you want to see those errors too. + + You may also want to see the [@google-cloud/error-reporting](https://github.com/googleapis/nodejs-error-reporting) module which provides direct access to the Error Reporting API. + + ### Error handling with a default callback + + The `LoggingWinston` class creates an instance of `LoggingCommon` which by default uses the `Log` class from `@google-cloud/logging` package to write log entries. + The `Log` class writes logs asynchronously and there are cases when log entries cannot be written and an error is + thrown - if error is not handled properly, it could crash the application. One possible way to handle the error is to provide a default callback + to the `LoggingWinston` constructor which will be used to initialize `Log` object with that callback like in example below: + + ```js + // Imports the Google Cloud client library for Winston + const {LoggingWinston} = require('@google-cloud/logging-winston'); + + // Creates a client + const loggingWinston = new LoggingWinston({ + projectId: 'your-project-id', + keyFilename: '/path/to/key.json', + defaultCallback: err => { + if (err) { + console.log('Error occured: ' + err); + } + }, + }); + ``` + + ### Formatting Request Logs + + **NOTE: The express middleware provided by this library handles this automatically for you. These instructions are for there case where you may want to handle this manually.** + + To format your request logs you can provide a `httpRequest` property as part of the log metadata you provide to winston. We will treat this as the [`HttpRequest`](https://cloud.google.com/logging/docs/reference/v2/rpc/google.logging.type#google.logging.type.HttpRequest) message and Cloud Logging will show this as a request log. Example: + + ![Request Log Example](https://raw.githubusercontent.com/googleapis/nodejs-logging-winston/master/doc/images/request-log.png) + + ```js + winston.info(`${req.url} endpoint hit`, { + httpRequest: { + status: res.statusCode, + requestUrl: req.url, + requestMethod: req.method, + remoteIp: req.connection.remoteAddress, + // etc. + } + }); + ``` + + The `httpRequest` property must be a properly formatted [`HttpRequest`](https://cloud.google.com/logging/docs/reference/v2/rpc/google.logging.type#google.logging.type.HttpRequest) message. + + **NOTE: Due to a bug in [logform](https://github.com/winstonjs/logform/issues/125) some built in Winston formatters might not work properly with `LoggingWinston`. For more information about the problem and possible workaround please see [540](https://github.com/googleapis/nodejs-logging-winston/issues/540). In addition, [Cloud Logging for Bunyan](https://github.com/googleapis/nodejs-logging-bunyan) could be considered as alternative. + + ### Correlating Logs with Traces + + **NOTE: The express middleware provided by this library handles this automatically for you. These instructions are for there case where you may want to handle this manually.** + + If you use [@google-cloud/trace-agent](https://www.npmjs.com/package/@google-cloud/trace-agent) module, then this module will set the Cloud Logging [LogEntry](https://cloud.google.com/logging/docs/reference/v2/rpc/google.logging.v2#logentry) `trace` property based on the current trace context when available. That correlation allows you to [view log entries](https://cloud.google.com/trace/docs/viewing-details#log_entries) inline with trace spans in the Cloud Trace Viewer. Example: + + ![Logs in Trace Example](https://raw.githubusercontent.com/googleapis/nodejs-logging-winston/master/doc/images/winston-logs-in-trace.png) + + If you wish to set the LogEntry `trace`, `spanId`, and `traceSampled` properties with custom values, then set Winston metadata properties for `'logging.googleapis.com/trace'`, `'logging.googleapis.com/spanId'`, `'logging.googleapis.com/trace_sampled'`, which is exported by this module as `LOGGING_TRACE_KEY`, `LOGGING_SPAN_KEY`, and `LOGGING_SAMPLED_KEY` respectively. For example: + + ```js + const winston = require('winston'); + const {LoggingWinston} = require('@google-cloud/logging-winston'); + + // ... + + winston.info('Log entry with custom trace value', { + [LoggingWinston.LOGGING_TRACE_KEY]: 'custom-trace-value' + [LoggingWinston.LOGGING_SPAN_KEY]: 'custom-span-value' + [LoggingWinston.LOGGING_SAMPLED_KEY]: true + }); + ``` + + ### Specifying default labels in the constructor + + You can specify `labels` when initiating the logger constructor. + + ```js + // Creates a Winston Cloud Logging client + const loggingWinston = new LoggingWinston({ + labels: { + name: 'some-name', + version: '0.1.0' + } + }); + + // Writes some log entries + logger.debug('test msg'); + + // you can also put some `labels` when calling the logger function + // the `labels` will be merge together + logger.debug('test msg', { + labels: { + module: 'some-module' + } + }); + ``` + + The `labels` will be on the Log Viewer. + + ![Request log with labels](https://raw.githubusercontent.com/googleapis/nodejs-logging-winston/master/doc/images/request-log-with-labels.png) + + ### Add a prefix to easily identify logs + + You can specify a `prefix` in the constructor, and that `prefix` will be prepended to all logging messages. This can be helpful, for example, to quickly identify logs from different modules in a project. + + ```js + // Creates a Winston Cloud Logging client + const loggingWinston = new LoggingWinston({ + prefix: 'some-module' + }); + + logger.debug('test msg'); + ``` + + ![Request log with prefix](https://raw.githubusercontent.com/googleapis/nodejs-logging-winston/master/doc/images/request-log-with-prefix.png) + + ### Alternative way to ingest logs in Google Cloud managed environments + If you use this library with the Cloud Logging Agent, you can configure the handler to output logs to `process.stdout` using + the [structured logging Json format](https://cloud.google.com/logging/docs/structured-logging#special-payload-fields). + To do this, add `redirectToStdout: true` parameter to the `LoggingWinston` constructor as in sample below. + You can use this parameter when running applications in Google Cloud managed environments such as AppEngine, Cloud Run, + Cloud Function or GKE. The logger agent installed on these environments can capture `process.stdout` and ingest it into Cloud Logging. + The agent can parse structured logs printed to `process.stdout` and capture additional log metadata beside the log payload. + It is recommended to set `redirectToStdout: true` in serverless environments like Cloud Functions since it could + decrease logging record loss upon execution termination - since all logs are written to `process.stdout` those + would be picked up by the Cloud Logging Agent running in Google Cloud managed environment. + Note that there is also a `useMessageField` option which controls if "message" field is used to store + structured, non-text data inside `jsonPayload` field when `redirectToStdout` is set. By default `useMessageField` is always `true`. + Set the `skipParentEntryForCloudRun` option to skip creating an entry for the request itself as Cloud Run already automatically creates + such log entries. This might become the default behaviour in a next major version. + + ```js + // Imports the Google Cloud client library for Winston + const {LoggingWinston} = require('@google-cloud/logging-winston'); + + // Creates a client that writes logs to stdout + const loggingWinston = new LoggingWinston({ + projectId: 'your-project-id', + keyFilename: '/path/to/key.json', + redirectToStdout: true, + }); + ``` + + ### Waiting for logs to be written + Starting from v3.0, the [Winston](https://github.com/winstonjs/winston/blob/master/UPGRADE-3.0.md#winstonlogger) library no longer supports + callbacks in their logging API, which reduces the ability to wait for logs to be written before exit/shutdown. The issue tracking the ask to reestablish callback support in Winston is tracked by [2095](https://github.com/winstonjs/winston/issues/2095). + One possible solution is to adopt an [Alternative way to ingest logs in Google Cloud managed environments](https://github.com/googleapis/nodejs-logging-winston#alternative-way-to-ingest-logs-in-google-cloud-managed-environments). + Another possible way is to use a `setTimeout` with a desired interval in order to let the library to send as many logs as possible. + diff --git a/handwritten/nodejs-logging-winston/.repo-metadata.json b/handwritten/nodejs-logging-winston/.repo-metadata.json new file mode 100644 index 00000000000..976306d16ea --- /dev/null +++ b/handwritten/nodejs-logging-winston/.repo-metadata.json @@ -0,0 +1,15 @@ +{ + "name": "logging-winston", + "name_pretty": "Cloud Logging for Winston", + "product_documentation": "https://cloud.google.com/logging", + "client_documentation": "https://cloud.google.com/nodejs/docs/reference/logging-winston/latest", + "issue_tracker": "https://issuetracker.google.com/savedsearches/559764", + "release_level": "stable", + "language": "nodejs", + "repo": "googleapis/google-cloud-node", + "distribution_name": "@google-cloud/logging-winston", + "api_id": "logging.googleapis.com", + "codeowner_team": "@googleapis/api-logging", + "api_shortname": "logging-winston", + "library_type": "OTHER" +} diff --git a/handwritten/nodejs-logging-winston/CHANGELOG.md b/handwritten/nodejs-logging-winston/CHANGELOG.md new file mode 100644 index 00000000000..286e9674f59 --- /dev/null +++ b/handwritten/nodejs-logging-winston/CHANGELOG.md @@ -0,0 +1,624 @@ +# Changelog + +[npm history][1] + +[1]: https://www.npmjs.com/package/@google-cloud/logging-winston?activeTab=versions + +## [6.0.1](https://github.com/googleapis/nodejs-logging-winston/compare/v6.0.0...v6.0.1) (2024-10-23) + + +### Bug Fixes + +* Fix trace_sampled type to be consistent with API documentation ([#830](https://github.com/googleapis/nodejs-logging-winston/issues/830)) ([33103dc](https://github.com/googleapis/nodejs-logging-winston/commit/33103dc073968d5b919c030ef035c2b317074134)) +* Skip writing RequestLogEntry for Cloud Run with flag enabled ([#821](https://github.com/googleapis/nodejs-logging-winston/issues/821)) ([7a0ec6f](https://github.com/googleapis/nodejs-logging-winston/commit/7a0ec6ffc1841487ab8f290de443f8a7465a2277)) + +## [6.0.0](https://github.com/googleapis/nodejs-logging-winston/compare/v5.3.0...v6.0.0) (2023-08-10) + + +### ⚠ BREAKING CHANGES + +* upgrade to node 14 ([#807](https://github.com/googleapis/nodejs-logging-winston/issues/807)) + +### Miscellaneous Chores + +* Upgrade to node 14 ([#807](https://github.com/googleapis/nodejs-logging-winston/issues/807)) ([2f4ea63](https://github.com/googleapis/nodejs-logging-winston/commit/2f4ea631a84d3d9da7a16f1f5922ceb95a4a62e4)) + +## [5.3.0](https://github.com/googleapis/nodejs-logging-winston/compare/v5.2.3...v5.3.0) (2023-01-04) + + +### Features + +* Add support for TransportStream options ([#775](https://github.com/googleapis/nodejs-logging-winston/issues/775)) ([cf240dd](https://github.com/googleapis/nodejs-logging-winston/commit/cf240ddd1edc04f5485b62f94c78c28a1b5614b6)) + +## [5.2.3](https://github.com/googleapis/nodejs-logging-winston/compare/v5.2.2...v5.2.3) (2022-12-02) + + +### Bug Fixes + +* Add a partner team as approvers for PRs ([#769](https://github.com/googleapis/nodejs-logging-winston/issues/769)) ([fbb96e0](https://github.com/googleapis/nodejs-logging-winston/commit/fbb96e0e6b54cd03774ed46e75cce6f52fb3c7a2)) + +## [5.2.2](https://github.com/googleapis/nodejs-logging-winston/compare/v5.2.1...v5.2.2) (2022-11-08) + + +### Bug Fixes + +* Unknown log level : http ([#766](https://github.com/googleapis/nodejs-logging-winston/issues/766)) ([167b89c](https://github.com/googleapis/nodejs-logging-winston/commit/167b89cd8b9fb0a20a8fd14e0831a46d566a5bb9)) + +## [5.2.1](https://github.com/googleapis/nodejs-logging-winston/compare/v5.2.0...v5.2.1) (2022-11-07) + + +### Bug Fixes + +* Switch instrumentation code to return version stored in NODEJS_WINSTON_DEFAULT_LIBRARY_VERSION ([#764](https://github.com/googleapis/nodejs-logging-winston/issues/764)) ([3e23e5a](https://github.com/googleapis/nodejs-logging-winston/commit/3e23e5aeef1ad8c1084e68d04fc2cf7b457fde34)) + +## [5.2.0](https://github.com/googleapis/nodejs-logging-winston/compare/v5.1.8...v5.2.0) (2022-11-04) + + +### Features + +* Add support for instrumentation version annotations ([#760](https://github.com/googleapis/nodejs-logging-winston/issues/760)) ([8237001](https://github.com/googleapis/nodejs-logging-winston/commit/82370012f29c610b6d4853f415159f70ee81f5b9)) + +## [5.1.8](https://github.com/googleapis/nodejs-logging-winston/compare/v5.1.7...v5.1.8) (2022-11-01) + + +### Bug Fixes + +* Prevent instrumentation crash and fix the system test ([#756](https://github.com/googleapis/nodejs-logging-winston/issues/756)) ([fdb6b26](https://github.com/googleapis/nodejs-logging-winston/commit/fdb6b26c2ed61158b0ee45f0419f788191047e1b)) + +## [5.1.7](https://github.com/googleapis/nodejs-logging-winston/compare/v5.1.6...v5.1.7) (2022-10-19) + + +### Bug Fixes + +* Default callback is not called upon error ([#750](https://github.com/googleapis/nodejs-logging-winston/issues/750)) ([737dc6c](https://github.com/googleapis/nodejs-logging-winston/commit/737dc6c66c0fd51a37d9953d06fba9f4473d99f7)) + +## [5.1.6](https://github.com/googleapis/nodejs-logging-winston/compare/v5.1.5...v5.1.6) (2022-10-13) + + +### Bug Fixes + +* Instrumentation performance ([#747](https://github.com/googleapis/nodejs-logging-winston/issues/747)) ([75daae8](https://github.com/googleapis/nodejs-logging-winston/commit/75daae87a1cc0589eca1044b218b777b551b9c18)) + +## [5.1.5](https://github.com/googleapis/nodejs-logging-winston/compare/v5.1.4...v5.1.5) (2022-09-02) + + +### Bug Fixes + +* MaxRetries parameter is ignored ([#737](https://github.com/googleapis/nodejs-logging-winston/issues/737)) ([f806489](https://github.com/googleapis/nodejs-logging-winston/commit/f806489f9f372beb467ee1cf3c83ad7bd6c2a23d)) + +## [5.1.4](https://github.com/googleapis/nodejs-logging-winston/compare/v5.1.3...v5.1.4) (2022-08-25) + + +### Bug Fixes + +* Update latest logging-nodejs to repaire google-gax vulnerability ([#733](https://github.com/googleapis/nodejs-logging-winston/issues/733)) ([5097ab5](https://github.com/googleapis/nodejs-logging-winston/commit/5097ab53e2a44aa09a164cd87c3ca424fdca682b)) + +## [5.1.3](https://github.com/googleapis/nodejs-logging-winston/compare/v5.1.2...v5.1.3) (2022-08-23) + + +### Bug Fixes + +* remove pip install statements ([#1546](https://github.com/googleapis/nodejs-logging-winston/issues/1546)) ([#730](https://github.com/googleapis/nodejs-logging-winston/issues/730)) ([750b207](https://github.com/googleapis/nodejs-logging-winston/commit/750b20747dace3da4807bf23b7c21212d42388c3)) + +## [5.1.2](https://github.com/googleapis/nodejs-logging-winston/compare/v5.1.1...v5.1.2) (2022-07-18) + + +### Bug Fixes + +* Logging to stdout in Cloud Run creates a JSON object as "message" ([#724](https://github.com/googleapis/nodejs-logging-winston/issues/724)) ([705f90e](https://github.com/googleapis/nodejs-logging-winston/commit/705f90e701dc76d0e30cbe5f296fb3401742618c)) + +## [5.1.1](https://github.com/googleapis/nodejs-logging-winston/compare/v5.1.0...v5.1.1) (2022-07-12) + + +### Bug Fixes + +* Remove Stackdriver references and add note regarding possible formatting issues ([#721](https://github.com/googleapis/nodejs-logging-winston/issues/721)) ([991ecb7](https://github.com/googleapis/nodejs-logging-winston/commit/991ecb7828a55a8fb55fc2e9b9e006c7419a5fd2)) + +## [5.1.0](https://github.com/googleapis/nodejs-logging-winston/compare/v5.0.0...v5.1.0) (2022-05-28) + + +### Features + +* Add support for library instrumentation ([#713](https://github.com/googleapis/nodejs-logging-winston/issues/713)) ([04f99b7](https://github.com/googleapis/nodejs-logging-winston/commit/04f99b7dd24b63cb4859b5d2413788d12522e33b)) + +## [5.0.0](https://github.com/googleapis/nodejs-logging-winston/compare/v4.2.4...v5.0.0) (2022-05-24) + + +### ⚠ BREAKING CHANGES + +* update library to use Node 12 (#706) + +### Bug Fixes + +* **deps:** update dependency @google-cloud/logging to v10 ([#709](https://github.com/googleapis/nodejs-logging-winston/issues/709)) ([1315964](https://github.com/googleapis/nodejs-logging-winston/commit/1315964fb1e93b9ca1ab41a407fed17ac34ad360)) + + +### Build System + +* update library to use Node 12 ([#706](https://github.com/googleapis/nodejs-logging-winston/issues/706)) ([63d6a93](https://github.com/googleapis/nodejs-logging-winston/commit/63d6a93d988f718acac2df507e4139b3b4ccf6b8)) + +### [4.2.4](https://github.com/googleapis/nodejs-logging-winston/compare/v4.2.3...v4.2.4) (2022-05-23) + + +### Bug Fixes + +* LoggingWinston log test 'should work correctly with winston formats' failed ([#710](https://github.com/googleapis/nodejs-logging-winston/issues/710)) ([9549b41](https://github.com/googleapis/nodejs-logging-winston/commit/9549b41614f3a0795f623c03755213c90520fb0f)) + +### [4.2.3](https://github.com/googleapis/nodejs-logging-winston/compare/v4.2.2...v4.2.3) (2022-04-21) + + +### Bug Fixes + +* Reenable staleness bot ([#691](https://github.com/googleapis/nodejs-logging-winston/issues/691)) ([2470dbc](https://github.com/googleapis/nodejs-logging-winston/commit/2470dbc2b225e77b78f8066477a2f63126e3b1cd)) + +### [4.2.2](https://github.com/googleapis/nodejs-logging-winston/compare/v4.2.1...v4.2.2) (2022-03-23) + + +### Bug Fixes + +* Error: write after end when handling SIGINT ([#684](https://github.com/googleapis/nodejs-logging-winston/issues/684)) ([acc1933](https://github.com/googleapis/nodejs-logging-winston/commit/acc19334037fdf3d7ff07597dc39514713e7264f)) + +### [4.2.1](https://github.com/googleapis/nodejs-logging-winston/compare/v4.2.0...v4.2.1) (2022-03-21) + + +### Bug Fixes + +* Add missing closure for code snippet in README ([#682](https://github.com/googleapis/nodejs-logging-winston/issues/682)) ([71158c8](https://github.com/googleapis/nodejs-logging-winston/commit/71158c8f2ef6a3fa2172db0728c8e07eb514abc5)) + +## [4.2.0](https://github.com/googleapis/nodejs-logging-winston/compare/v4.1.3...v4.2.0) (2022-03-18) + + +### Features + +* Add support to print structured logging to STDOUT ([#676](https://github.com/googleapis/nodejs-logging-winston/issues/676)) ([76135ca](https://github.com/googleapis/nodejs-logging-winston/commit/76135ca81cf0a2e6e48bc34e4b982daa64ce2cd9)) + +### [4.1.3](https://github.com/googleapis/nodejs-logging-winston/compare/v4.1.2...v4.1.3) (2022-03-09) + + +### Bug Fixes + +* Use defaultCallback in LoggingCommon class ([#672](https://github.com/googleapis/nodejs-logging-winston/issues/672)) ([4bc7baa](https://github.com/googleapis/nodejs-logging-winston/commit/4bc7baaa2dcc5d67e2c524f21bfe0c80ef99e8c4)) + +### [4.1.2](https://github.com/googleapis/nodejs-logging-winston/compare/v4.1.1...v4.1.2) (2022-02-16) + + +### Bug Fixes + +* Update dependency @google-cloud/logging from 9.0.0 to 9.6.9 ([#667](https://github.com/googleapis/nodejs-logging-winston/issues/667)) ([6fcda1e](https://github.com/googleapis/nodejs-logging-winston/commit/6fcda1e641546fcf948b2fe1a0eb1ebc1f83e11c)) + +### [4.1.1](https://www.github.com/googleapis/nodejs-logging-winston/compare/v4.1.0...v4.1.1) (2021-09-08) + + +### Bug Fixes + +* **build:** update branch to main ([#624](https://www.github.com/googleapis/nodejs-logging-winston/issues/624)) ([00771be](https://www.github.com/googleapis/nodejs-logging-winston/commit/00771beeeaaa99772455defa6aa92f47d9a5cba1)) + +## [4.1.0](https://www.github.com/googleapis/nodejs-logging-winston/compare/v4.0.5...v4.1.0) (2021-06-14) + + +### Features + +* propagate spanIds ([#599](https://www.github.com/googleapis/nodejs-logging-winston/issues/599)) ([6a34151](https://www.github.com/googleapis/nodejs-logging-winston/commit/6a341511ec195282d14bb05ccae7752c91bdd36c)) + +### [4.0.5](https://www.github.com/googleapis/nodejs-logging-winston/compare/v4.0.4...v4.0.5) (2021-05-10) + + +### Miscellaneous Chores + +* release 4.0.5 ([#590](https://www.github.com/googleapis/nodejs-logging-winston/issues/590)) ([565684a](https://www.github.com/googleapis/nodejs-logging-winston/commit/565684a710b10402756b452fd6fd5fde1652b274)) + +### [4.0.4](https://www.github.com/googleapis/nodejs-logging-winston/compare/v4.0.3...v4.0.4) (2021-02-09) + + +### Bug Fixes + +* **deps:** update dependency google-auth-library to v7 ([#561](https://www.github.com/googleapis/nodejs-logging-winston/issues/561)) ([e5ec00b](https://www.github.com/googleapis/nodejs-logging-winston/commit/e5ec00b536f0351abd32110fe8882a394fec55e7)) + +### [4.0.3](https://www.github.com/googleapis/nodejs-logging-winston/compare/v4.0.2...v4.0.3) (2021-01-21) + + +### Bug Fixes + +* dedupe logs when using middleware ([#553](https://www.github.com/googleapis/nodejs-logging-winston/issues/553)) ([34c9a47](https://www.github.com/googleapis/nodejs-logging-winston/commit/34c9a47e19420b2163b86862732b75a1275e37c9)) + +### [4.0.2](https://www.github.com/googleapis/nodejs-logging-winston/compare/v4.0.1...v4.0.2) (2020-12-08) + + +### Bug Fixes + +* **deps:** update dependency @google-cloud/logging to v9 ([#550](https://www.github.com/googleapis/nodejs-logging-winston/issues/550)) ([b9d9852](https://www.github.com/googleapis/nodejs-logging-winston/commit/b9d9852a80796ddf235900bca796a5909e74a7d1)) + +### [4.0.1](https://www.github.com/googleapis/nodejs-logging-winston/compare/v4.0.0...v4.0.1) (2020-11-03) + + +### Bug Fixes + +* add user provided transport to the logger ([#543](https://www.github.com/googleapis/nodejs-logging-winston/issues/543)) ([b789429](https://www.github.com/googleapis/nodejs-logging-winston/commit/b789429b95c9d94ceeed57bf55e7f6cf8fab109a)) + +## [4.0.0](https://www.github.com/googleapis/nodejs-logging-winston/compare/v3.0.6...v4.0.0) (2020-07-10) + + +### ⚠ BREAKING CHANGES + +* drop support for node.js 8.x (#495) + +### Features + +* **secrets:** begin migration to secret manager from keystore ([#509](https://www.github.com/googleapis/nodejs-logging-winston/issues/509)) ([df7240d](https://www.github.com/googleapis/nodejs-logging-winston/commit/df7240d89de8cc6d1e5b50238476b9e41d1da549)) + + +### Bug Fixes + +* apache license URL ([#468](https://www.github.com/googleapis/nodejs-logging-winston/issues/468)) ([#491](https://www.github.com/googleapis/nodejs-logging-winston/issues/491)) ([db70cc4](https://www.github.com/googleapis/nodejs-logging-winston/commit/db70cc4aa103aea292f16313b564f67d479b481d)) +* **deps:** update dependency @google-cloud/logging to v8 ([#513](https://www.github.com/googleapis/nodejs-logging-winston/issues/513)) ([2dcb12d](https://www.github.com/googleapis/nodejs-logging-winston/commit/2dcb12d85d9b5abdf28a745f00e0e566ffb39d66)) +* **deps:** update dependency google-auth-library to v6 ([#485](https://www.github.com/googleapis/nodejs-logging-winston/issues/485)) ([7b16a7b](https://www.github.com/googleapis/nodejs-logging-winston/commit/7b16a7b184ba9ec0ac8a1a103feac84c0374cfa6)) + + +### Build System + +* drop support for node.js 8.x ([#495](https://www.github.com/googleapis/nodejs-logging-winston/issues/495)) ([42fc0e5](https://www.github.com/googleapis/nodejs-logging-winston/commit/42fc0e5bccb68fe92c08678a6ea471073b1b7162)) + +### [3.0.6](https://www.github.com/googleapis/nodejs-logging-winston/compare/v3.0.5...v3.0.6) (2020-03-02) + + +### Bug Fixes + +* correct a typo in a code comment ([#463](https://www.github.com/googleapis/nodejs-logging-winston/issues/463)) ([2b29d62](https://www.github.com/googleapis/nodejs-logging-winston/commit/2b29d62f98a6c29334ffa03ebadb7909fb027ca0)) + +### [3.0.5](https://www.github.com/googleapis/nodejs-logging-winston/compare/v3.0.4...v3.0.5) (2020-01-13) + + +### Bug Fixes + +* **deps:** update dependency @google-cloud/logging to v7 ([#439](https://www.github.com/googleapis/nodejs-logging-winston/issues/439)) ([ac69ff4](https://www.github.com/googleapis/nodejs-logging-winston/commit/ac69ff488084a615a616582b1fcb450456653760)) + +### [3.0.4](https://www.github.com/googleapis/nodejs-logging-winston/compare/v3.0.3...v3.0.4) (2020-01-04) + + +### Bug Fixes + +* **deps:** update dependency @google-cloud/logging to v6 ([#421](https://www.github.com/googleapis/nodejs-logging-winston/issues/421)) ([d43bf17](https://www.github.com/googleapis/nodejs-logging-winston/commit/d43bf1778b903f311ae49e6c5a6bad542e21c5da)) + +### [3.0.3](https://www.github.com/googleapis/nodejs-logging-winston/compare/v3.0.2...v3.0.3) (2020-01-02) + + +### Bug Fixes + +* **docs:** bump release level to GA ([#416](https://www.github.com/googleapis/nodejs-logging-winston/issues/416)) ([4832f31](https://www.github.com/googleapis/nodejs-logging-winston/commit/4832f310c149119c2f4ece980015fd2f11d5cb08)) + +### [3.0.2](https://www.github.com/googleapis/nodejs-logging-winston/compare/v3.0.1...v3.0.2) (2019-12-12) + + +### Bug Fixes + +* **docs:** link to Stackdriver Error Reporting ([#424](https://www.github.com/googleapis/nodejs-logging-winston/issues/424)) ([19ba68f](https://www.github.com/googleapis/nodejs-logging-winston/commit/19ba68f2b0cb3eb1ef314dd156ef3995a0a0faaa)) + +### [3.0.1](https://www.github.com/googleapis/nodejs-logging-winston/compare/v3.0.0...v3.0.1) (2019-12-05) + + +### Bug Fixes + +* **deps:** pin TypeScript below 3.7.0 ([cf58882](https://www.github.com/googleapis/nodejs-logging-winston/commit/cf58882c1345792b0b0309362769e6636342ceea)) +* **docs:** add jsdoc-region-tag plugin ([#408](https://www.github.com/googleapis/nodejs-logging-winston/issues/408)) ([973ab56](https://www.github.com/googleapis/nodejs-logging-winston/commit/973ab56ddaadcdb76a930c9f6bd4662023b714e3)) + +## [3.0.0](https://www.github.com/googleapis/nodejs-logging-winston/compare/v2.1.0...v3.0.0) (2019-10-18) + + +### ⚠ BREAKING CHANGES + +* logs now truncate to 256,000ish bytes by default (#404) + +### Features + +* logs now truncate to 256,000ish bytes by default ([#404](https://www.github.com/googleapis/nodejs-logging-winston/issues/404)) ([bea68ae](https://www.github.com/googleapis/nodejs-logging-winston/commit/bea68ae5fd22b2d1cf52b12832bb6288ac8d3570)) + +## [2.1.0](https://www.github.com/googleapis/nodejs-logging-winston/compare/v2.0.1...v2.1.0) (2019-09-19) + + +### Bug Fixes + +* **deps:** explicit update to logging dependency see: [#392](https://www.github.com/googleapis/nodejs-logging-winston/issues/392) ([#394](https://www.github.com/googleapis/nodejs-logging-winston/issues/394)) ([134be02](https://www.github.com/googleapis/nodejs-logging-winston/commit/134be02)) + + +### Features + +* add getDefaultMetadataForTracing ([#388](https://www.github.com/googleapis/nodejs-logging-winston/issues/388)) ([f6bd9dd](https://www.github.com/googleapis/nodejs-logging-winston/commit/f6bd9dd)) + +### [2.0.1](https://www.github.com/googleapis/nodejs-logging-winston/compare/v2.0.0...v2.0.1) (2019-08-28) + + +### Bug Fixes + +* **docs:** stop linking reference documents to anchor ([4bc78ab](https://www.github.com/googleapis/nodejs-logging-winston/commit/4bc78ab)) + +## [2.0.0](https://www.github.com/googleapis/nodejs-logging-winston/compare/v1.1.1...v2.0.0) (2019-08-21) + + +### ⚠ BREAKING CHANGES + +* **middleware:** We now promote the `logEntry` metadata field in a +winston log info object to be the `logName` reported to Stackdriver. This means +that the logs will show up under the log name specified by the `logName`. In addition +there are several breaking changes to users of the express middleware: + +* The middleware function has been replaced by makeMiddleware. +* makeMiddleware expects a winston logger to be passed in. +* Previously, we would append a `_applog` suffix to the user provided + application log name. We no longer do that. We use the user provided + log name for the application logs. The request logs now have a suffix. + +Rationale: +Let the middleware users provide a winston logger that we annotate with a transport +rather than creating two winston loggers on user's behalf. We avoid the +need for having two transports by pomoting the `logName` field from the +winston metadata into the LogEntry. This allows a child logger to write +to a different stackdriver log stream - as needed for request bundling. + +### Bug Fixes + +* **deps:** update dependency google-auth-library to v5 ([#375](https://www.github.com/googleapis/nodejs-logging-winston/issues/375)) ([f0ec607](https://www.github.com/googleapis/nodejs-logging-winston/commit/f0ec607)) +* **docs:** generate correct examples in README.md ([#376](https://www.github.com/googleapis/nodejs-logging-winston/issues/376)) ([7517462](https://www.github.com/googleapis/nodejs-logging-winston/commit/7517462)) +* **middleware:** allow users to provide middleware ([#369](https://www.github.com/googleapis/nodejs-logging-winston/issues/369)) ([e61dad9](https://www.github.com/googleapis/nodejs-logging-winston/commit/e61dad9)) +* **middleware:** use winston provided child logger api ([#359](https://www.github.com/googleapis/nodejs-logging-winston/issues/359)) ([5d83565](https://www.github.com/googleapis/nodejs-logging-winston/commit/5d83565)) + + +### Features + +* **middleware:** make API more ergonomic ([#364](https://www.github.com/googleapis/nodejs-logging-winston/issues/364)) ([5ecdeb4](https://www.github.com/googleapis/nodejs-logging-winston/commit/5ecdeb4)) + +### [1.1.1](https://www.github.com/googleapis/nodejs-logging-winston/compare/v1.1.0...v1.1.1) (2019-06-26) + + +### Bug Fixes + +* **docs:** link to reference docs section on googleapis.dev ([#356](https://www.github.com/googleapis/nodejs-logging-winston/issues/356)) ([9deaea3](https://www.github.com/googleapis/nodejs-logging-winston/commit/9deaea3)) + +## [1.1.0](https://www.github.com/googleapis/nodejs-logging-winston/compare/v1.0.1...v1.1.0) (2019-06-21) + + +### Bug Fixes + +* **docs:** pin to version of compodoc that renders JavaScript/tables ([#344](https://www.github.com/googleapis/nodejs-logging-winston/issues/344)) ([ea9ea9a](https://www.github.com/googleapis/nodejs-logging-winston/commit/ea9ea9a)) +* properly import transport-stream ([#348](https://www.github.com/googleapis/nodejs-logging-winston/issues/348)) ([7eec9dc](https://www.github.com/googleapis/nodejs-logging-winston/commit/7eec9dc)), closes [#341](https://www.github.com/googleapis/nodejs-logging-winston/issues/341) [#342](https://www.github.com/googleapis/nodejs-logging-winston/issues/342) +* **middleware:** need message in request log message ([#349](https://www.github.com/googleapis/nodejs-logging-winston/issues/349)) ([cb11e4c](https://www.github.com/googleapis/nodejs-logging-winston/commit/cb11e4c)) + + +### Features + +* add support for apiEndpoint override ([#352](https://www.github.com/googleapis/nodejs-logging-winston/issues/352)) ([6666276](https://www.github.com/googleapis/nodejs-logging-winston/commit/6666276)) + +### [1.0.1](https://www.github.com/googleapis/nodejs-logging-winston/compare/v1.0.0...v1.0.1) (2019-06-15) + + +### Bug Fixes + +* **docs:** move to new client docs URL ([#339](https://www.github.com/googleapis/nodejs-logging-winston/issues/339)) ([c9f8d13](https://www.github.com/googleapis/nodejs-logging-winston/commit/c9f8d13)) + +## [1.0.0](https://www.github.com/googleapis/nodejs-logging-winston/compare/v0.11.1...v1.0.0) (2019-06-11) + + +### ⚠ BREAKING CHANGES + +* upgrade engines field to >=8.10.0 (#308) +* Node 6 is no longer tested. +* only support winston3 (#297) + +### Bug Fixes + +* expose express middleware, add system-test ([#278](https://www.github.com/googleapis/nodejs-logging-winston/issues/278)) ([dc17ad7](https://www.github.com/googleapis/nodejs-logging-winston/commit/dc17ad7)) +* use immutable winston level ([#319](https://www.github.com/googleapis/nodejs-logging-winston/issues/319)) ([450295f](https://www.github.com/googleapis/nodejs-logging-winston/commit/450295f)) +* **deps:** bump minimum required dependencies ([#336](https://www.github.com/googleapis/nodejs-logging-winston/issues/336)) ([fc87d65](https://www.github.com/googleapis/nodejs-logging-winston/commit/fc87d65)) +* **deps:** update dependency @google-cloud/logging to v5 ([#324](https://www.github.com/googleapis/nodejs-logging-winston/issues/324)) ([059c2a1](https://www.github.com/googleapis/nodejs-logging-winston/commit/059c2a1)) +* **deps:** update dependency google-auth-library to v4 ([#317](https://www.github.com/googleapis/nodejs-logging-winston/issues/317)) ([6182968](https://www.github.com/googleapis/nodejs-logging-winston/commit/6182968)) + + +### Build System + +* upgrade engines field to >=8.10.0 ([#308](https://www.github.com/googleapis/nodejs-logging-winston/issues/308)) ([c777689](https://www.github.com/googleapis/nodejs-logging-winston/commit/c777689)) + + +### Code Refactoring + +* only support winston3 ([#297](https://www.github.com/googleapis/nodejs-logging-winston/issues/297)) ([9d3911b](https://www.github.com/googleapis/nodejs-logging-winston/commit/9d3911b)) + + +### Miscellaneous Chores + +* drop node 6 support ([#307](https://www.github.com/googleapis/nodejs-logging-winston/issues/307)) ([08631b7](https://www.github.com/googleapis/nodejs-logging-winston/commit/08631b7)) + +## v0.11.1 + +04-09-2019 17:34 PDT + +### Bug Fixes + +- fix: add missing dep on google-auth-library +- fix: only copy timestamp metadata if it is a date ([#295](https://github.com/googleapis/nodejs-logging-winston/pull/295)) +- fix: assign timestamps from log metadata ([#294](https://github.com/googleapis/nodejs-logging-winston/pull/294)) + +### Dependencies + +- chore(deps): update dependency @types/semver to v6 +- fix(deps): update dependency semver to v6 +- chore(deps): update dependency mocha to v6 ([#269](https://github.com/googleapis/nodejs-logging-winston/pull/269)) + +### Internal / Testing Changes + +- refactor: use execSync for tests ([#292](https://github.com/googleapis/nodejs-logging-winston/pull/292)) +- chore: publish to npm using wombat ([#283](https://github.com/googleapis/nodejs-logging-winston/pull/283)) +- test: error-reporting system test improvement ([#282](https://github.com/googleapis/nodejs-logging-winston/pull/282)) +- test: make error reporting system test more robust +- test: fix error reporting system test race +- build: Add docuploader credentials to node publish jobs ([#274](https://github.com/googleapis/nodejs-logging-winston/pull/274)) +- refactor: wrap execSync with encoding: utf-8 +- build: use per-repo npm publish token +- build: update release config ([#271](https://github.com/googleapis/nodejs-logging-winston/pull/271)) +- build: use node10 to run samples-test, system-test etc ([#273](https://github.com/googleapis/nodejs-logging-winston/pull/273)) + +## v0.11.0 + +02-15-2019 10:42 PST + +### Features +- feature: request logging middleware for express ([#182](https://github.com/googleapis/nodejs-logging-winston/pull/182)) + +### Bug Fixes +- fix: remove circular references ([#264](https://github.com/googleapis/nodejs-logging-winston/pull/264)) + +### Dependencies +- fix(deps): update dependency logform to v2 ([#247](https://github.com/googleapis/nodejs-logging-winston/pull/247)) +- fix(deps): update dependency @sindresorhus/is to ^0.13.0 ([#213](https://github.com/googleapis/nodejs-logging-winston/pull/213)) +- fix(deps): update dependency @sindresorhus/is to ^0.12.0 ([#126](https://github.com/googleapis/nodejs-logging-winston/pull/126)) + +### Documentation +- docs: update links in contrib guide ([#267](https://github.com/googleapis/nodejs-logging-winston/pull/267)) +- docs: update contributing path in README ([#260](https://github.com/googleapis/nodejs-logging-winston/pull/260)) +- docs: add lint/fix example to contributing guide ([#255](https://github.com/googleapis/nodejs-logging-winston/pull/255)) +- docs: update readme badges ([#229](https://github.com/googleapis/nodejs-logging-winston/pull/229)) + +### Internal / Testing Changes +- build: use linkinator for docs test ([#266](https://github.com/googleapis/nodejs-logging-winston/pull/266)) +- fix: de-flake system tests ([#265](https://github.com/googleapis/nodejs-logging-winston/pull/265)) +- build: create docs test npm scripts ([#263](https://github.com/googleapis/nodejs-logging-winston/pull/263)) +- build: test using @grpc/grpc-js in CI ([#261](https://github.com/googleapis/nodejs-logging-winston/pull/261)) +- chore: move CONTRIBUTING.md to root ([#259](https://github.com/googleapis/nodejs-logging-winston/pull/259)) +- chore(deps): update dependency @google-cloud/common to ^0.31.0 ([#256](https://github.com/googleapis/nodejs-logging-winston/pull/256)) +- chore: update @google-cloud/common to 0.30.2 ([#254](https://github.com/googleapis/nodejs-logging-winston/pull/254)) +- chore(deps): update dependency eslint-config-prettier to v4 ([#253](https://github.com/googleapis/nodejs-logging-winston/pull/253)) +- build: ignore googleapis.com in doc link check ([#251](https://github.com/googleapis/nodejs-logging-winston/pull/251)) +- chore(build): check for 404s when publishing docs ([#248](https://github.com/googleapis/nodejs-logging-winston/pull/248)) +- refactor: update sample test dependencies ([#246](https://github.com/googleapis/nodejs-logging-winston/pull/246)) +- chore(build): inject yoshi automation key ([#243](https://github.com/googleapis/nodejs-logging-winston/pull/243)) +- chore: update nyc and eslint configs ([#242](https://github.com/googleapis/nodejs-logging-winston/pull/242)) +- chore: fix publish.sh permission +x ([#240](https://github.com/googleapis/nodejs-logging-winston/pull/240)) +- fix(build): fix Kokoro release script ([#239](https://github.com/googleapis/nodejs-logging-winston/pull/239)) +- build: add Kokoro configs for autorelease ([#238](https://github.com/googleapis/nodejs-logging-winston/pull/238)) +- chore: update system tests key ([#232](https://github.com/googleapis/nodejs-logging-winston/pull/232)) +- chore: always nyc report before calling codecov ([#235](https://github.com/googleapis/nodejs-logging-winston/pull/235)) +- chore: nyc ignore build/test by default ([#234](https://github.com/googleapis/nodejs-logging-winston/pull/234)) +- chore: update license file ([#231](https://github.com/googleapis/nodejs-logging-winston/pull/231)) +- fix(build): fix system key decryption ([#227](https://github.com/googleapis/nodejs-logging-winston/pull/227)) +- chore: update key for system tests ([#226](https://github.com/googleapis/nodejs-logging-winston/pull/226)) +- chore(deps): update dependency @google-cloud/common to ^0.27.0 ([#225](https://github.com/googleapis/nodejs-logging-winston/pull/225)) +- refactor: reduce the number of dependencies ([#222](https://github.com/googleapis/nodejs-logging-winston/pull/222)) +- chore: add a synth.metadata +- chore(deps): update dependency gts to ^0.9.0 ([#219](https://github.com/googleapis/nodejs-logging-winston/pull/219)) +- chore: update eslintignore config ([#218](https://github.com/googleapis/nodejs-logging-winston/pull/218)) +- docs(samples): convert samples test from ava to mocha ([#207](https://github.com/googleapis/nodejs-logging-winston/pull/207)) +- chore(deps): update dependency @google-cloud/nodejs-repo-tools to v3 ([#217](https://github.com/googleapis/nodejs-logging-winston/pull/217)) +- chore: drop contributors from multiple places ([#216](https://github.com/googleapis/nodejs-logging-winston/pull/216)) +- chore(deps): update dependency @types/is to v0.0.21 ([#215](https://github.com/googleapis/nodejs-logging-winston/pull/215)) +- chore: use latest npm on Windows ([#214](https://github.com/googleapis/nodejs-logging-winston/pull/214)) +- chore: use unique UUID per system test ([#212](https://github.com/googleapis/nodejs-logging-winston/pull/212)) +- chore: update CircleCI config ([#211](https://github.com/googleapis/nodejs-logging-winston/pull/211)) +- chore: include build in eslintignore ([#208](https://github.com/googleapis/nodejs-logging-winston/pull/208)) +- chore(deps): update dependency eslint-plugin-node to v8 ([#203](https://github.com/googleapis/nodejs-logging-winston/pull/203)) +- chore: update issue templates ([#202](https://github.com/googleapis/nodejs-logging-winston/pull/202)) +- chore: remove old issue template ([#200](https://github.com/googleapis/nodejs-logging-winston/pull/200)) +- build: run tests on node11 ([#199](https://github.com/googleapis/nodejs-logging-winston/pull/199)) +- chore(deps): update dependency @google-cloud/common to ^0.26.0 ([#198](https://github.com/googleapis/nodejs-logging-winston/pull/198)) +- chores(build): do not collect sponge.xml from windows builds ([#197](https://github.com/googleapis/nodejs-logging-winston/pull/197)) +- chores(build): run codecov on continuous builds ([#196](https://github.com/googleapis/nodejs-logging-winston/pull/196)) +- chore: update new issue template ([#195](https://github.com/googleapis/nodejs-logging-winston/pull/195)) +- build: fix codecov uploading on Kokoro ([#192](https://github.com/googleapis/nodejs-logging-winston/pull/192)) + +## v0.10.2 + +### Fixes +- fix: Doesnt set service context winston3 ([#180](https://github.com/googleapis/nodejs-logging-winston/pull/180)) +- fix: Don't publish sourcemaps ([#178](https://github.com/googleapis/nodejs-logging-winston/pull/178)) + +### Internal / Testing Changes +- test: increasing error_reporting poll timeout for system tests ([#187](https://github.com/googleapis/nodejs-logging-winston/pull/187)) +- Update kokoro config ([#185](https://github.com/googleapis/nodejs-logging-winston/pull/185)) +- chore(deps): update dependency eslint-plugin-prettier to v3 ([#183](https://github.com/googleapis/nodejs-logging-winston/pull/183)) + +## v0.10.1 + +### Documentation +- Add missing @class docstring ([#176](https://github.com/googleapis/nodejs-logging-winston/pull/176)) + +## v0.10.0 + +**This release has breaking changes**. Support for node.js 4.x and 9.x has ended. + +### Breaking Changes +- fix: drop support for node.js 9.x ([#122](https://github.com/googleapis/nodejs-logging-winston/pull/122)) +- chore: drop node 4 support ([#97](https://github.com/googleapis/nodejs-logging-winston/pull/97)) + +### New Features +- feat: Winston2 and 3 support. ([#161](https://github.com/googleapis/nodejs-logging-winston/pull/161)) +- feat: use small HTTP dependency ([#153](https://github.com/googleapis/nodejs-logging-winston/pull/153)) + +### Bug Fixes +- fix: logged errors are reported to error reporting ([#148](https://github.com/googleapis/nodejs-logging-winston/pull/148)) +- doc: fix link to HttpRequest message ([#99](https://github.com/googleapis/nodejs-logging-winston/pull/99)) +- fix: prevent permanent merging of labels ([#89](https://github.com/googleapis/nodejs-logging-winston/pull/89)) +- fix: remove unnecessary runtime dependencies ([#73](https://github.com/googleapis/nodejs-logging-winston/pull/73)) +- fix: Fix typo in readme ([#69](https://github.com/googleapis/nodejs-logging-winston/pull/69)) + +### Dependencies +- fix: Upgrade to @google-cloud/logging 4.x ([#168](https://github.com/googleapis/nodejs-logging-winston/pull/168)) +- chore(deps): update dependency @google-cloud/common to ^0.25.0 ([#163](https://github.com/googleapis/nodejs-logging-winston/pull/163)) +- chore(deps): update dependency delay to v4 ([#154](https://github.com/googleapis/nodejs-logging-winston/pull/154)) +- chore(deps): update dependency @google-cloud/common to ^0.24.0 ([#158](https://github.com/googleapis/nodejs-logging-winston/pull/158)) +- fix(deps): update dependency @google-cloud/common to ^0.23.0 and logging. ([#152](https://github.com/googleapis/nodejs-logging-winston/pull/152)) +- fix(deps): update dependency @google-cloud/logging to v3 ([#149](https://github.com/googleapis/nodejs-logging-winston/pull/149)) +- chore(deps): update dependency pify to v4 ([#145](https://github.com/googleapis/nodejs-logging-winston/pull/145)) +- fix(deps): update dependency @google-cloud/logging to v2 ([#121](https://github.com/googleapis/nodejs-logging-winston/pull/121)) + +### Internal / Testing Changes +- Update CI config ([#171](https://github.com/googleapis/nodejs-logging-winston/pull/171)) +- Enable prefer-const in the eslint config ([#166](https://github.com/googleapis/nodejs-logging-winston/pull/166)) +- chore(deps): update dependency @types/glob to v7 ([#167](https://github.com/googleapis/nodejs-logging-winston/pull/167)) +- fix: fixing samples test and guarding for no entries in system test ([#165](https://github.com/googleapis/nodejs-logging-winston/pull/165)) +- Enable no-var in eslint ([#162](https://github.com/googleapis/nodejs-logging-winston/pull/162)) +- fix: presystem-test should func pretest ([#164](https://github.com/googleapis/nodejs-logging-winston/pull/164)) +- Update CI config ([#160](https://github.com/googleapis/nodejs-logging-winston/pull/160)) +- Add synth script and update CI ([#156](https://github.com/googleapis/nodejs-logging-winston/pull/156)) +- Retry npm install in CI ([#155](https://github.com/googleapis/nodejs-logging-winston/pull/155)) +- chore(deps): update dependency eslint-config-prettier to v3 ([#146](https://github.com/googleapis/nodejs-logging-winston/pull/146)) +- chore: ignore package-lock.json ([#144](https://github.com/googleapis/nodejs-logging-winston/pull/144)) +- chore(deps): lock file maintenance ([#143](https://github.com/googleapis/nodejs-logging-winston/pull/143)) +- chore(deps): lock file maintenance ([#142](https://github.com/googleapis/nodejs-logging-winston/pull/142)) +- chore(deps): lock file maintenance ([#141](https://github.com/googleapis/nodejs-logging-winston/pull/141)) +- chore: update renovate config ([#140](https://github.com/googleapis/nodejs-logging-winston/pull/140)) +- chore: remove greenkeeper badge ([#139](https://github.com/googleapis/nodejs-logging-winston/pull/139)) +- test: throw on deprecation ([#138](https://github.com/googleapis/nodejs-logging-winston/pull/138)) +- chore(deps): lock file maintenance ([#137](https://github.com/googleapis/nodejs-logging-winston/pull/137)) +- chore(deps): update dependency typescript to v3 ([#136](https://github.com/googleapis/nodejs-logging-winston/pull/136)) +- chore: assert.deelEqual => assert.deepStrictEqual ([#135](https://github.com/googleapis/nodejs-logging-winston/pull/135)) +- chore: move mocha options to mocha.opts ([#133](https://github.com/googleapis/nodejs-logging-winston/pull/133)) +- chore: require node 8 for samples ([#134](https://github.com/googleapis/nodejs-logging-winston/pull/134)) +- chore(deps): lock file maintenance ([#132](https://github.com/googleapis/nodejs-logging-winston/pull/132)) +- chore(deps): lock file maintenance ([#131](https://github.com/googleapis/nodejs-logging-winston/pull/131)) +- chore(deps): update dependency eslint-plugin-node to v7 ([#130](https://github.com/googleapis/nodejs-logging-winston/pull/130)) +- chore(deps): lock file maintenance ([#128](https://github.com/googleapis/nodejs-logging-winston/pull/128)) +- chore(deps): update dependency gts to ^0.8.0 ([#127](https://github.com/googleapis/nodejs-logging-winston/pull/127)) +- chore(deps): lock file maintenance ([#125](https://github.com/googleapis/nodejs-logging-winston/pull/125)) +- chore(deps): lock file maintenance ([#124](https://github.com/googleapis/nodejs-logging-winston/pull/124)) +- chore(deps): lock file maintenance ([#123](https://github.com/googleapis/nodejs-logging-winston/pull/123)) +- fix(deps): update dependency @sindresorhus/is to ^0.10.0 ([#120](https://github.com/googleapis/nodejs-logging-winston/pull/120)) +- chore(deps): lock file maintenance ([#119](https://github.com/googleapis/nodejs-logging-winston/pull/119)) +- chore(deps): lock file maintenance ([#118](https://github.com/googleapis/nodejs-logging-winston/pull/118)) +- fix(deps): update dependency yargs to v12 ([#117](https://github.com/googleapis/nodejs-logging-winston/pull/117)) +- chore: update packages ([#114](https://github.com/googleapis/nodejs-logging-winston/pull/114)) +- chore(deps): update dependency ava to v0.25.0 ([#110](https://github.com/googleapis/nodejs-logging-winston/pull/110)) +- Configure Renovate ([#102](https://github.com/googleapis/nodejs-logging-winston/pull/102)) +- chore(package): update eslint to version 5.0.0 ([#103](https://github.com/googleapis/nodejs-logging-winston/pull/103)) +- refactor: drop repo-tool as an exec wrapper ([#107](https://github.com/googleapis/nodejs-logging-winston/pull/107)) +- chore: update sample lockfiles ([#106](https://github.com/googleapis/nodejs-logging-winston/pull/106)) +- fix: update linking for samples ([#104](https://github.com/googleapis/nodejs-logging-winston/pull/104)) +- cleanup: get rid of unncessary type casts ([#101](https://github.com/googleapis/nodejs-logging-winston/pull/101)) +- chore(package): update cpy-cli to version 2.0.0 ([#86](https://github.com/googleapis/nodejs-logging-winston/pull/86)) +- chore(package): update to the latest gts and typescript ([#100](https://github.com/googleapis/nodejs-logging-winston/pull/100)) +- test: fix race between sample and system tests ([#98](https://github.com/googleapis/nodejs-logging-winston/pull/98)) +- fix: fix broken install tests ([#96](https://github.com/googleapis/nodejs-logging-winston/pull/96)) +- chore: remove `--bail` from system tests config ([#95](https://github.com/googleapis/nodejs-logging-winston/pull/95)) +- chore: lock files maintenance ([#83](https://github.com/googleapis/nodejs-logging-winston/pull/83)) +- chore: the ultimate fix for repo-tools EPERM ([#82](https://github.com/googleapis/nodejs-logging-winston/pull/82)) +- chore: timeout for system test ([#81](https://github.com/googleapis/nodejs-logging-winston/pull/81)) +- chore(package): update @types/node to version 10.0.9 ([#80](https://github.com/googleapis/nodejs-logging-winston/pull/80)) +- chore: lock files maintenance ([#79](https://github.com/googleapis/nodejs-logging-winston/pull/79)) +- chore: test on node10 ([#77](https://github.com/googleapis/nodejs-logging-winston/pull/77)) +- chore: lock files maintenance ([#75](https://github.com/googleapis/nodejs-logging-winston/pull/75)) diff --git a/handwritten/nodejs-logging-winston/CODE_OF_CONDUCT.md b/handwritten/nodejs-logging-winston/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..2add2547a81 --- /dev/null +++ b/handwritten/nodejs-logging-winston/CODE_OF_CONDUCT.md @@ -0,0 +1,94 @@ + +# Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of +experience, education, socio-economic status, nationality, personal appearance, +race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, or to ban temporarily or permanently any +contributor for other behaviors that they deem inappropriate, threatening, +offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +This Code of Conduct also applies outside the project spaces when the Project +Steward has a reasonable belief that an individual's behavior may have a +negative impact on the project or its community. + +## Conflict Resolution + +We do not believe that all conflict is bad; healthy debate and disagreement +often yield positive results. However, it is never okay to be disrespectful or +to engage in behavior that violates the project’s code of conduct. + +If you see someone violating the code of conduct, you are encouraged to address +the behavior directly with those involved. Many issues can be resolved quickly +and easily, and this gives people more control over the outcome of their +dispute. If you are unable to resolve the matter for any reason, or if the +behavior is threatening or harassing, report it. We are dedicated to providing +an environment where participants feel welcome and safe. + +Reports should be directed to *googleapis-stewards@google.com*, the +Project Steward(s) for *Google Cloud Client Libraries*. It is the Project Steward’s duty to +receive and address reported violations of the code of conduct. They will then +work with a committee consisting of representatives from the Open Source +Programs Office and the Google Open Source Strategy team. If for any reason you +are uncomfortable reaching out to the Project Steward, please email +opensource@google.com. + +We will investigate every complaint, but you may not receive a direct response. +We will use our discretion in determining when and how to follow up on reported +incidents, which may range from not taking action to permanent expulsion from +the project and project-sponsored spaces. We will notify the accused of the +report and provide them an opportunity to discuss it before any action is taken. +The identity of the reporter will be omitted from the details of the report +supplied to the accused. In potentially harmful situations, such as ongoing +harassment or threats to anyone's safety, we may take action without notice. + +## Attribution + +This Code of Conduct is adapted from the Contributor Covenant, version 1.4, +available at +https://www.contributor-covenant.org/version/1/4/code-of-conduct.html \ No newline at end of file diff --git a/handwritten/nodejs-logging-winston/CONTRIBUTING.md b/handwritten/nodejs-logging-winston/CONTRIBUTING.md new file mode 100644 index 00000000000..e28195c2015 --- /dev/null +++ b/handwritten/nodejs-logging-winston/CONTRIBUTING.md @@ -0,0 +1,75 @@ +# How to become a contributor and submit your own code + +**Table of contents** + +* [Contributor License Agreements](#contributor-license-agreements) +* [Contributing a patch](#contributing-a-patch) +* [Running the tests](#running-the-tests) +* [Releasing the library](#releasing-the-library) + +## Contributor License Agreements + +We'd love to accept your sample apps and patches! Before we can take them, we +have to jump a couple of legal hurdles. + +Please fill out either the individual or corporate Contributor License Agreement +(CLA). + + * If you are an individual writing original source code and you're sure you + own the intellectual property, then you'll need to sign an [individual CLA](https://developers.google.com/open-source/cla/individual). + * If you work for a company that wants to allow you to contribute your work, + then you'll need to sign a [corporate CLA](https://developers.google.com/open-source/cla/corporate). + +Follow either of the two links above to access the appropriate CLA and +instructions for how to sign and return it. Once we receive it, we'll be able to +accept your pull requests. + +## Contributing A Patch + +1. Submit an issue describing your proposed change to the repo in question. +1. The repo owner will respond to your issue promptly. +1. If your proposed change is accepted, and you haven't already done so, sign a + Contributor License Agreement (see details above). +1. Fork the desired repo, develop and test your code changes. +1. Ensure that your code adheres to the existing style in the code to which + you are contributing. +1. Ensure that your code has an appropriate set of tests which all pass. +1. Title your pull request following [Conventional Commits](https://www.conventionalcommits.org/) styling. +1. Submit a pull request. + +### Before you begin + +1. [Select or create a Cloud Platform project][projects]. +1. [Enable the Cloud Logging for Winston API][enable_api]. +1. [Set up authentication with a service account][auth] so you can access the + API from your local workstation. + + +## Running the tests + +1. [Prepare your environment for Node.js setup][setup]. + +1. Install dependencies: + + npm install + +1. Run the tests: + + # Run unit tests. + npm test + + # Run sample integration tests. + npm run samples-test + + # Run all system tests. + npm run system-test + +1. Lint (and maybe fix) any changes: + + npm run fix + +[setup]: https://cloud.google.com/nodejs/docs/setup +[projects]: https://console.cloud.google.com/project +[billing]: https://support.google.com/cloud/answer/6293499#enable-billing +[enable_api]: https://console.cloud.google.com/flows/enableapi?apiid=logging.googleapis.com +[auth]: https://cloud.google.com/docs/authentication/getting-started \ No newline at end of file diff --git a/handwritten/nodejs-logging-winston/LICENSE b/handwritten/nodejs-logging-winston/LICENSE new file mode 100644 index 00000000000..d6456956733 --- /dev/null +++ b/handwritten/nodejs-logging-winston/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/handwritten/nodejs-logging-winston/README.md b/handwritten/nodejs-logging-winston/README.md new file mode 100644 index 00000000000..4795c14778c --- /dev/null +++ b/handwritten/nodejs-logging-winston/README.md @@ -0,0 +1,405 @@ +[//]: # "This README.md file is auto-generated, all changes to this file will be lost." +[//]: # "To regenerate it, use `python -m synthtool`." +Google Cloud Platform logo + +# [Cloud Logging for Winston: Node.js Client](https://github.com/googleapis/nodejs-logging-winston) + +[![release level](https://img.shields.io/badge/release%20level-stable-brightgreen.svg?style=flat)](https://cloud.google.com/terms/launch-stages) +[![npm version](https://img.shields.io/npm/v/@google-cloud/logging-winston.svg)](https://www.npmjs.com/package/@google-cloud/logging-winston) + + + + +This module provides a higher-level layer for working with +[Cloud Logging](https://cloud.google.com/logging/docs), compatible with +[Winston](https://www.npmjs.com/package/winston). Simply attach this as a +transport to your existing Winston loggers. + + +A comprehensive list of changes in each version may be found in +[the CHANGELOG](https://github.com/googleapis/nodejs-logging-winston/blob/main/CHANGELOG.md). + +* [Cloud Logging for Winston Node.js Client API Reference][client-docs] +* [Cloud Logging for Winston Documentation][product-docs] +* [github.com/googleapis/nodejs-logging-winston](https://github.com/googleapis/nodejs-logging-winston) + +Read more about the client libraries for Cloud APIs, including the older +Google APIs Client Libraries, in [Client Libraries Explained][explained]. + +[explained]: https://cloud.google.com/apis/docs/client-libraries-explained + +**Table of contents:** + + +* [Quickstart](#quickstart) + * [Before you begin](#before-you-begin) + * [Installing the client library](#installing-the-client-library) + * [Using the client library](#using-the-client-library) +* [Samples](#samples) +* [Versioning](#versioning) +* [Contributing](#contributing) +* [License](#license) + +## Quickstart + +### Before you begin + +1. [Select or create a Cloud Platform project][projects]. +1. [Enable the Cloud Logging for Winston API][enable_api]. +1. [Set up authentication][auth] so you can access the + API from your local workstation. + +### Installing the client library + +```bash +npm install @google-cloud/logging-winston +``` + + +### Using the client library + +```javascript +const winston = require('winston'); + +// Imports the Google Cloud client library for Winston +const {LoggingWinston} = require('@google-cloud/logging-winston'); + +const loggingWinston = new LoggingWinston(); + +// Create a Winston logger that streams to Cloud Logging +// Logs will be written to: "projects/YOUR_PROJECT_ID/logs/winston_log" +const logger = winston.createLogger({ + level: 'info', + transports: [ + new winston.transports.Console(), + // Add Cloud Logging + loggingWinston, + ], +}); + +// Writes some log entries +logger.error('warp nacelles offline'); +logger.info('shields at 99%'); + +``` +For a more detailed Cloud Logging setup guide, see https://cloud.google.com/logging/docs/setup/nodejs. + +Creates a Winston logger that streams to Cloud Logging + +Logs will be written to: "projects/YOUR_PROJECT_ID/logs/winston_log" + +### Using as an express middleware + +***NOTE: this feature is experimental. The API may change in a backwards +incompatible way until this is deemed stable. Please provide us feedback so +that we can better refine this express integration.*** + +We provide a middleware that can be used in an express application. Apart from +being easy to use, this enables some more powerful features of Cloud +Logging: request bundling. Any application logs emitted on behalf of a specific +request will be shown nested inside the request log as you see in this +screenshot: + +![Request Bundling Example](https://raw.githubusercontent.com/googleapis/nodejs-logging-winston/master/doc/images/request-bundling.png) + +This middleware adds a `winston`-style log function to the `request` object. +You can use this wherever you have access to the `request` object (`req` in the +sample below). All log entries that are made on behalf of a specific request are +shown bundled together in the Cloud Logging UI. + +```javascript +const lw = require('@google-cloud/logging-winston'); +const winston = require('winston'); + +// Import express module and create an http server. +const express = require('express'); +const logger = winston.createLogger(); + +async function main() { + // Create a middleware that will use the provided logger. + // A Cloud Logging transport will be created automatically + // and added onto the provided logger. + const mw = await lw.express.makeMiddleware(logger); + // Alternatively, you can construct a LoggingWinston transport + // yourself and pass it int. + // const transport = new LoggingWinston({...}); + // const mw = await lw.express.makeMiddleware(logger, transport); + + const app = express(); + + // Install the logging middleware. This ensures that a Winston-style `log` + // function is available on the `request` object. Attach this as one of the + // earliest middleware to make sure that the log function is available in all + // subsequent middleware and routes. + app.use(mw); + + // Setup an http route and a route handler. + app.get('/', (req, res) => { + // `req.log` can be used as a winston style log method. All logs generated + // using `req.log` use the current request context. That is, all logs + // corresponding to a specific request will be bundled in the Cloud Logging + // UI. + req.log.info('this is an info log message'); + res.send('hello world'); + }); + + // `logger` can be used as a global logger, one not correlated to any specific + // request. + logger.info('bonjour'); + + // Start listening on the http server. + app.listen(8080, () => { + logger.info('http server listening on port 8080'); + }); +} + +main(); +``` + +### Error Reporting + +Any `Error` objects you log at severity `error` or higher can automatically be picked up by [Error Reporting](https://cloud.google.com/error-reporting/) if you have specified a `serviceContext.service` when instantiating a `LoggingWinston` instance: + +```javascript +const loggingWinston = new LoggingWinston({ +serviceContext: { + service: 'my-service', // required to report logged errors + // to the Error Reporting + // console + version: 'my-version' +} +}); +``` + +It is an error to specify a `serviceContext` but not specify `serviceContext.service`. + +Make sure to add logs to your [uncaught exception](https://nodejs.org/api/process.html#process_event_uncaughtexception) and [unhandled rejection](https://nodejs.org/api/process.html#process_event_unhandledrejection) handlers if you want to see those errors too. + +You may also want to see the [@google-cloud/error-reporting](https://github.com/googleapis/nodejs-error-reporting) module which provides direct access to the Error Reporting API. + +### Error handling with a default callback + +The `LoggingWinston` class creates an instance of `LoggingCommon` which by default uses the `Log` class from `@google-cloud/logging` package to write log entries. +The `Log` class writes logs asynchronously and there are cases when log entries cannot be written and an error is +thrown - if error is not handled properly, it could crash the application. One possible way to handle the error is to provide a default callback +to the `LoggingWinston` constructor which will be used to initialize `Log` object with that callback like in example below: + +```js +// Imports the Google Cloud client library for Winston +const {LoggingWinston} = require('@google-cloud/logging-winston'); + +// Creates a client +const loggingWinston = new LoggingWinston({ +projectId: 'your-project-id', +keyFilename: '/path/to/key.json', +defaultCallback: err => { + if (err) { + console.log('Error occured: ' + err); + } +}, +}); +``` + +### Formatting Request Logs + +**NOTE: The express middleware provided by this library handles this automatically for you. These instructions are for there case where you may want to handle this manually.** + +To format your request logs you can provide a `httpRequest` property as part of the log metadata you provide to winston. We will treat this as the [`HttpRequest`](https://cloud.google.com/logging/docs/reference/v2/rpc/google.logging.type#google.logging.type.HttpRequest) message and Cloud Logging will show this as a request log. Example: + +![Request Log Example](https://raw.githubusercontent.com/googleapis/nodejs-logging-winston/master/doc/images/request-log.png) + +```js +winston.info(`${req.url} endpoint hit`, { +httpRequest: { + status: res.statusCode, + requestUrl: req.url, + requestMethod: req.method, + remoteIp: req.connection.remoteAddress, + // etc. +} +}); +``` + +The `httpRequest` property must be a properly formatted [`HttpRequest`](https://cloud.google.com/logging/docs/reference/v2/rpc/google.logging.type#google.logging.type.HttpRequest) message. + +**NOTE: Due to a bug in [logform](https://github.com/winstonjs/logform/issues/125) some built in Winston formatters might not work properly with `LoggingWinston`. For more information about the problem and possible workaround please see [540](https://github.com/googleapis/nodejs-logging-winston/issues/540). In addition, [Cloud Logging for Bunyan](https://github.com/googleapis/nodejs-logging-bunyan) could be considered as alternative. + +### Correlating Logs with Traces + +**NOTE: The express middleware provided by this library handles this automatically for you. These instructions are for there case where you may want to handle this manually.** + +If you use [@google-cloud/trace-agent](https://www.npmjs.com/package/@google-cloud/trace-agent) module, then this module will set the Cloud Logging [LogEntry](https://cloud.google.com/logging/docs/reference/v2/rpc/google.logging.v2#logentry) `trace` property based on the current trace context when available. That correlation allows you to [view log entries](https://cloud.google.com/trace/docs/viewing-details#log_entries) inline with trace spans in the Cloud Trace Viewer. Example: + +![Logs in Trace Example](https://raw.githubusercontent.com/googleapis/nodejs-logging-winston/master/doc/images/winston-logs-in-trace.png) + +If you wish to set the LogEntry `trace`, `spanId`, and `traceSampled` properties with custom values, then set Winston metadata properties for `'logging.googleapis.com/trace'`, `'logging.googleapis.com/spanId'`, `'logging.googleapis.com/trace_sampled'`, which is exported by this module as `LOGGING_TRACE_KEY`, `LOGGING_SPAN_KEY`, and `LOGGING_SAMPLED_KEY` respectively. For example: + +```js +const winston = require('winston'); +const {LoggingWinston} = require('@google-cloud/logging-winston'); + +// ... + +winston.info('Log entry with custom trace value', { +[LoggingWinston.LOGGING_TRACE_KEY]: 'custom-trace-value' +[LoggingWinston.LOGGING_SPAN_KEY]: 'custom-span-value' +[LoggingWinston.LOGGING_SAMPLED_KEY]: true +}); +``` + +### Specifying default labels in the constructor + +You can specify `labels` when initiating the logger constructor. + +```js +// Creates a Winston Cloud Logging client +const loggingWinston = new LoggingWinston({ +labels: { + name: 'some-name', + version: '0.1.0' +} +}); + +// Writes some log entries +logger.debug('test msg'); + +// you can also put some `labels` when calling the logger function +// the `labels` will be merge together +logger.debug('test msg', { +labels: { + module: 'some-module' +} +}); +``` + +The `labels` will be on the Log Viewer. + +![Request log with labels](https://raw.githubusercontent.com/googleapis/nodejs-logging-winston/master/doc/images/request-log-with-labels.png) + +### Add a prefix to easily identify logs + +You can specify a `prefix` in the constructor, and that `prefix` will be prepended to all logging messages. This can be helpful, for example, to quickly identify logs from different modules in a project. + +```js +// Creates a Winston Cloud Logging client +const loggingWinston = new LoggingWinston({ +prefix: 'some-module' +}); + +logger.debug('test msg'); +``` + +![Request log with prefix](https://raw.githubusercontent.com/googleapis/nodejs-logging-winston/master/doc/images/request-log-with-prefix.png) + +### Alternative way to ingest logs in Google Cloud managed environments +If you use this library with the Cloud Logging Agent, you can configure the handler to output logs to `process.stdout` using +the [structured logging Json format](https://cloud.google.com/logging/docs/structured-logging#special-payload-fields). +To do this, add `redirectToStdout: true` parameter to the `LoggingWinston` constructor as in sample below. +You can use this parameter when running applications in Google Cloud managed environments such as AppEngine, Cloud Run, +Cloud Function or GKE. The logger agent installed on these environments can capture `process.stdout` and ingest it into Cloud Logging. +The agent can parse structured logs printed to `process.stdout` and capture additional log metadata beside the log payload. +It is recommended to set `redirectToStdout: true` in serverless environments like Cloud Functions since it could +decrease logging record loss upon execution termination - since all logs are written to `process.stdout` those +would be picked up by the Cloud Logging Agent running in Google Cloud managed environment. +Note that there is also a `useMessageField` option which controls if "message" field is used to store +structured, non-text data inside `jsonPayload` field when `redirectToStdout` is set. By default `useMessageField` is always `true`. +Set the `skipParentEntryForCloudRun` option to skip creating an entry for the request itself as Cloud Run already automatically creates +such log entries. This might become the default behaviour in a next major version. + +```js +// Imports the Google Cloud client library for Winston +const {LoggingWinston} = require('@google-cloud/logging-winston'); + +// Creates a client that writes logs to stdout +const loggingWinston = new LoggingWinston({ + projectId: 'your-project-id', + keyFilename: '/path/to/key.json', + redirectToStdout: true, +}); +``` + +### Waiting for logs to be written +Starting from v3.0, the [Winston](https://github.com/winstonjs/winston/blob/master/UPGRADE-3.0.md#winstonlogger) library no longer supports +callbacks in their logging API, which reduces the ability to wait for logs to be written before exit/shutdown. The issue tracking the ask to reestablish callback support in Winston is tracked by [2095](https://github.com/winstonjs/winston/issues/2095). +One possible solution is to adopt an [Alternative way to ingest logs in Google Cloud managed environments](https://github.com/googleapis/nodejs-logging-winston#alternative-way-to-ingest-logs-in-google-cloud-managed-environments). +Another possible way is to use a `setTimeout` with a desired interval in order to let the library to send as many logs as possible. + + +## Samples + +Samples are in the [`samples/`](https://github.com/googleapis/nodejs-logging-winston/tree/main/samples) directory. Each sample's `README.md` has instructions for running its sample. + +| Sample | Source Code | Try it | +| --------------------------- | --------------------------------- | ------ | +| Quickstart | [source code](https://github.com/googleapis/nodejs-logging-winston/blob/main/samples/quickstart.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-logging-winston&page=editor&open_in_editor=samples/quickstart.js,samples/README.md) | +| Explicit Auth Setup | [source code](https://github.com/googleapis/nodejs-logging-winston/blob/main/samples/setup_explicit.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-logging-winston&page=editor&open_in_editor=samples/setup_explicit.js,samples/README.md) | + + + +The [Cloud Logging for Winston Node.js Client API Reference][client-docs] documentation +also contains samples. + +## Supported Node.js Versions + +Our client libraries follow the [Node.js release schedule](https://github.com/nodejs/release#release-schedule). +Libraries are compatible with all current _active_ and _maintenance_ versions of +Node.js. +If you are using an end-of-life version of Node.js, we recommend that you update +as soon as possible to an actively supported LTS version. + +Google's client libraries support legacy versions of Node.js runtimes on a +best-efforts basis with the following warnings: + +* Legacy versions are not tested in continuous integration. +* Some security patches and features cannot be backported. +* Dependencies cannot be kept up-to-date. + +Client libraries targeting some end-of-life versions of Node.js are available, and +can be installed through npm [dist-tags](https://docs.npmjs.com/cli/dist-tag). +The dist-tags follow the naming convention `legacy-(version)`. +For example, `npm install @google-cloud/logging-winston@legacy-8` installs client libraries +for versions compatible with Node.js 8. + +## Versioning + +This library follows [Semantic Versioning](http://semver.org/). + + + +This library is considered to be **stable**. The code surface will not change in backwards-incompatible ways +unless absolutely necessary (e.g. because of critical security issues) or with +an extensive deprecation period. Issues and requests against **stable** libraries +are addressed with the highest priority. + + + + + + +More Information: [Google Cloud Platform Launch Stages][launch_stages] + +[launch_stages]: https://cloud.google.com/terms/launch-stages + +## Contributing + +Contributions welcome! See the [Contributing Guide](https://github.com/googleapis/nodejs-logging-winston/blob/main/CONTRIBUTING.md). + +Please note that this `README.md`, the `samples/README.md`, +and a variety of configuration files in this repository (including `.nycrc` and `tsconfig.json`) +are generated from a central template. To edit one of these files, make an edit +to its templates in +[directory](https://github.com/googleapis/synthtool). + +## License + +Apache Version 2.0 + +See [LICENSE](https://github.com/googleapis/nodejs-logging-winston/blob/main/LICENSE) + +[client-docs]: https://cloud.google.com/nodejs/docs/reference/logging-winston/latest +[product-docs]: https://cloud.google.com/logging +[shell_img]: https://gstatic.com/cloudssh/images/open-btn.png +[projects]: https://console.cloud.google.com/project +[billing]: https://support.google.com/cloud/answer/6293499#enable-billing +[enable_api]: https://console.cloud.google.com/flows/enableapi?apiid=logging.googleapis.com +[auth]: https://cloud.google.com/docs/authentication/external/set-up-adc-local diff --git a/handwritten/nodejs-logging-winston/doc/images/request-bundling.png b/handwritten/nodejs-logging-winston/doc/images/request-bundling.png new file mode 100644 index 00000000000..e81e2be34bc Binary files /dev/null and b/handwritten/nodejs-logging-winston/doc/images/request-bundling.png differ diff --git a/handwritten/nodejs-logging-winston/doc/images/request-log-with-labels.png b/handwritten/nodejs-logging-winston/doc/images/request-log-with-labels.png new file mode 100644 index 00000000000..0bf503841a7 Binary files /dev/null and b/handwritten/nodejs-logging-winston/doc/images/request-log-with-labels.png differ diff --git a/handwritten/nodejs-logging-winston/doc/images/request-log-with-prefix.png b/handwritten/nodejs-logging-winston/doc/images/request-log-with-prefix.png new file mode 100644 index 00000000000..274577497c7 Binary files /dev/null and b/handwritten/nodejs-logging-winston/doc/images/request-log-with-prefix.png differ diff --git a/handwritten/nodejs-logging-winston/doc/images/request-log.png b/handwritten/nodejs-logging-winston/doc/images/request-log.png new file mode 100644 index 00000000000..a58853afa15 Binary files /dev/null and b/handwritten/nodejs-logging-winston/doc/images/request-log.png differ diff --git a/handwritten/nodejs-logging-winston/doc/images/winston-logs-in-trace.png b/handwritten/nodejs-logging-winston/doc/images/winston-logs-in-trace.png new file mode 100644 index 00000000000..a0189359928 Binary files /dev/null and b/handwritten/nodejs-logging-winston/doc/images/winston-logs-in-trace.png differ diff --git a/handwritten/nodejs-logging-winston/linkinator.config.json b/handwritten/nodejs-logging-winston/linkinator.config.json new file mode 100644 index 00000000000..29a223b6db6 --- /dev/null +++ b/handwritten/nodejs-logging-winston/linkinator.config.json @@ -0,0 +1,10 @@ +{ + "recurse": true, + "skip": [ + "https://codecov.io/gh/googleapis/", + "www.googleapis.com", + "img.shields.io" + ], + "silent": true, + "concurrency": 10 +} diff --git a/handwritten/nodejs-logging-winston/owlbot.py b/handwritten/nodejs-logging-winston/owlbot.py new file mode 100644 index 00000000000..a92b1acb9cb --- /dev/null +++ b/handwritten/nodejs-logging-winston/owlbot.py @@ -0,0 +1,54 @@ +# Copyright 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import synthtool as s +import synthtool.gcp as gcp +import synthtool.languages.node_mono_repo as node +import logging +import os + +logging.basicConfig(level=logging.DEBUG) + +common_templates = gcp.CommonTemplates() +templates = common_templates.node_library() +s.copy(templates, excludes=[ + ".github/auto-label.yaml", + ".github/release-please.yml", + ".github/CODEOWNERS", + ".github/sync-repo-settings.yaml", + ".github/workflows/ci.yaml", + ".kokoro" +]) +node.fix() + + +# -------------------------------------------------------------------------- +# Modify test configs +# -------------------------------------------------------------------------- + +# add shared environment variables to test configs +s.move( + ".kokoro/common_env_vars.cfg", + ".kokoro/common.cfg", + merge=lambda src, dst, _, : f"{dst}\n{src}", +) +for path, subdirs, files in os.walk(f".kokoro/continuous"): + for name in files: + if name == "common.cfg": + file_path = os.path.join(path, name) + s.move( + ".kokoro/common_env_vars.cfg", + file_path, + merge=lambda src, dst, _, : f"{dst}\n{src}", + ) diff --git a/handwritten/nodejs-logging-winston/package.json b/handwritten/nodejs-logging-winston/package.json new file mode 100644 index 00000000000..e27e13c0e7e --- /dev/null +++ b/handwritten/nodejs-logging-winston/package.json @@ -0,0 +1,87 @@ +{ + "name": "@google-cloud/logging-winston", + "description": "Cloud Logging transport for Winston", + "version": "6.0.1", + "license": "Apache-2.0", + "author": "Google Inc.", + "engines": { + "node": ">=18" + }, + "repository": { + "type": "git", + "directory": "handwritten/nodejs-logging-winston", + "url": "https://github.com/googleapis/google-cloud-node.git" + }, + "main": "./build/src/index.js", + "types": "./build/src/index.d.ts", + "files": [ + "build/src", + "!build/src/**/*.map", + "CODE_OF_CONDUCT.md" + ], + "keywords": [ + "google apis client", + "google api client", + "google apis", + "google api", + "google", + "google cloud platform", + "google cloud", + "cloud", + "google logging", + "logging", + "cloud logging", + "cloud", + "winston transport", + "winston" + ], + "scripts": { + "docs": "jsdoc -c .jsdoc.js", + "lint": "gts check", + "presamples-test": "npm run compile", + "samples-test": "cd samples/ && npm link ../ && npm test && cd ../", + "presystem-test": "npm run pretest", + "system-test": "mocha build/system-test --timeout 600000", + "test": "c8 mocha --recursive build/test", + "clean": "gts clean", + "compile": "tsc -p .", + "fix": "gts fix", + "prepare": "npm run compile", + "pretest": "npm run compile", + "docs-test": "linkinator docs", + "predocs-test": "npm run docs", + "prelint": "cd samples; npm link ../; npm install", + "precompile": "gts clean" + }, + "dependencies": { + "@google-cloud/logging": "^11.2.1", + "google-auth-library": "^10.5.0", + "lodash.mapvalues": "^4.6.0", + "winston-transport": "^4.9.0" + }, + "devDependencies": { + "@google-cloud/common": "^6.0.0", + "@types/lodash.mapvalues": "^4.6.9", + "@types/mocha": "^10.0.10", + "@types/node": "^24.10.1", + "@types/proxyquire": "^1.3.31", + "@types/triple-beam": "^1.3.5", + "c8": "^10.1.3", + "codecov": "^3.8.3", + "gts": "^6.0.2", + "jsdoc": "^4.0.5", + "jsdoc-fresh": "^5.0.2", + "jsdoc-region-tag": "^4.0.1", + "linkinator": "^6.0.0", + "mocha": "^11.7.5", + "pack-n-play": "4.2.1", + "post-install-check": "^0.0.1", + "proxyquire": "^2.1.3", + "typescript": "^5.9.3", + "winston": "^3.18.3" + }, + "peerDependencies": { + "winston": ">=3.2.1" + }, + "homepage": "https://github.com/googleapis/google-cloud-node/tree/main/handwritten/nodejs-logging-winston" +} diff --git a/handwritten/nodejs-logging-winston/samples/.eslintrc.yml b/handwritten/nodejs-logging-winston/samples/.eslintrc.yml new file mode 100644 index 00000000000..282535f55f6 --- /dev/null +++ b/handwritten/nodejs-logging-winston/samples/.eslintrc.yml @@ -0,0 +1,3 @@ +--- +rules: + no-console: off diff --git a/handwritten/nodejs-logging-winston/samples/README.md b/handwritten/nodejs-logging-winston/samples/README.md new file mode 100644 index 00000000000..ae9d313c28f --- /dev/null +++ b/handwritten/nodejs-logging-winston/samples/README.md @@ -0,0 +1,71 @@ +[//]: # "This README.md file is auto-generated, all changes to this file will be lost." +[//]: # "To regenerate it, use `python -m synthtool`." +Google Cloud Platform logo + +# [Cloud Logging for Winston: Node.js Samples](https://github.com/googleapis/nodejs-logging-winston) + +[![Open in Cloud Shell][shell_img]][shell_link] + +This module provides a higher-level layer for working with +[Cloud Logging](https://cloud.google.com/logging/docs), compatible with +[Winston](https://www.npmjs.com/package/winston). Simply attach this as a +transport to your existing Winston loggers. + +## Table of Contents + +* [Before you begin](#before-you-begin) +* [Samples](#samples) + * [Quickstart](#quickstart) + * [Explicit Auth Setup](#explicit-auth-setup) + +## Before you begin + +Before running the samples, make sure you've followed the steps outlined in +[Using the client library](https://github.com/googleapis/nodejs-logging-winston#using-the-client-library). + +`cd samples` + +`npm install` + +`cd ..` + +## Samples + + + +### Quickstart + +View the [source code](https://github.com/googleapis/nodejs-logging-winston/blob/main/samples/quickstart.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-logging-winston&page=editor&open_in_editor=samples/quickstart.js,samples/README.md) + +__Usage:__ + + +`node samples/quickstart.js` + + +----- + + + + +### Explicit Auth Setup + +View the [source code](https://github.com/googleapis/nodejs-logging-winston/blob/main/samples/setup_explicit.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-logging-winston&page=editor&open_in_editor=samples/setup_explicit.js,samples/README.md) + +__Usage:__ + + +`node samples/setup_explicit.js` + + + + + + +[shell_img]: https://gstatic.com/cloudssh/images/open-btn.png +[shell_link]: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-logging-winston&page=editor&open_in_editor=samples/README.md +[product-docs]: https://cloud.google.com/logging diff --git a/handwritten/nodejs-logging-winston/samples/package.json b/handwritten/nodejs-logging-winston/samples/package.json new file mode 100644 index 00000000000..bcb5694998c --- /dev/null +++ b/handwritten/nodejs-logging-winston/samples/package.json @@ -0,0 +1,24 @@ +{ + "name": "nodejs-docs-samples-logging-winston", + "files": [ + "*.js" + ], + "private": true, + "license": "Apache-2.0", + "author": "Google Inc.", + "repository": "googleapis/nodejs-logging-winston", + "engines": { + "node": ">=18" + }, + "scripts": { + "test": "mocha --timeout 600000" + }, + "dependencies": { + "@google-cloud/logging-winston": "^6.0.1", + "winston": "^3.2.1" + }, + "devDependencies": { + "chai": "^4.2.0", + "mocha": "^8.0.0" + } +} \ No newline at end of file diff --git a/handwritten/nodejs-logging-winston/samples/quickstart.js b/handwritten/nodejs-logging-winston/samples/quickstart.js new file mode 100644 index 00000000000..2bd7c36b35b --- /dev/null +++ b/handwritten/nodejs-logging-winston/samples/quickstart.js @@ -0,0 +1,39 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +// [START logging_winston_quickstart] +const winston = require('winston'); + +// Imports the Google Cloud client library for Winston +const {LoggingWinston} = require('@google-cloud/logging-winston'); + +const loggingWinston = new LoggingWinston(); + +// Create a Winston logger that streams to Cloud Logging +// Logs will be written to: "projects/YOUR_PROJECT_ID/logs/winston_log" +const logger = winston.createLogger({ + level: 'info', + transports: [ + new winston.transports.Console(), + // Add Cloud Logging + loggingWinston, + ], +}); + +// Writes some log entries +logger.error('warp nacelles offline'); +logger.info('shields at 99%'); +// [END logging_winston_quickstart] diff --git a/handwritten/nodejs-logging-winston/samples/setup_explicit.js b/handwritten/nodejs-logging-winston/samples/setup_explicit.js new file mode 100644 index 00000000000..06c55704c1b --- /dev/null +++ b/handwritten/nodejs-logging-winston/samples/setup_explicit.js @@ -0,0 +1,31 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Explicit Auth Setup + +'use strict'; + +/* eslint-disable no-unused-vars */ +// [START logging_winston_setup_explicit] +// Imports the Google Cloud client library for Winston +const {LoggingWinston} = require('@google-cloud/logging-winston'); + +// Creates a client +const loggingWinston = new LoggingWinston({ + projectId: 'your-project-id', + keyFilename: '/path/to/key.json', +}); +// [END logging_winston_setup_explicit] +/* eslint-enable no-unused-vars */ diff --git a/handwritten/nodejs-logging-winston/samples/test/quickstart.test.js b/handwritten/nodejs-logging-winston/samples/test/quickstart.test.js new file mode 100644 index 00000000000..72a67d91d7f --- /dev/null +++ b/handwritten/nodejs-logging-winston/samples/test/quickstart.test.js @@ -0,0 +1,31 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const path = require('path'); +const {assert} = require('chai'); +const {describe, it} = require('mocha'); +const cp = require('child_process'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +describe('QuickStart', () => { + it('should write using winston', async () => { + const stdout = execSync('node quickstart.js', { + cwd: path.join(__dirname, '..'), + }); + assert.match(stdout, /99%/); + }); +}); diff --git a/handwritten/nodejs-logging-winston/src/common.ts b/handwritten/nodejs-logging-winston/src/common.ts new file mode 100644 index 00000000000..4507a266de6 --- /dev/null +++ b/handwritten/nodejs-logging-winston/src/common.ts @@ -0,0 +1,369 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as util from 'util'; +import { + Logging, + protos, + ServiceContext, + SeverityNames, + Log, + LogSync, +} from '@google-cloud/logging'; +import { + setInstrumentationStatus, + createDiagnosticEntry, +} from '@google-cloud/logging/build/src/utils/instrumentation'; +import {LogSeverityFunctions} from '@google-cloud/logging/build/src/utils/log-common'; +import mapValues = require('lodash.mapvalues'); +import {Options} from '.'; +import {Entry, LogEntry} from '@google-cloud/logging/build/src/entry'; +import {LogSyncOptions} from '@google-cloud/logging/build/src/log-sync'; + +type Callback = (err: Error | null, apiResponse?: {}) => void; +export type MonitoredResource = protos.google.api.MonitoredResource; + +export interface StackdriverData { + serviceContext?: ServiceContext; + message?: string; + metadata?: Metadata | MetadataArg; +} + +export interface Metadata { + stack?: string; + httpRequest?: protos.google.logging.type.IHttpRequest; + labels?: {}; + // And arbitrary other properties. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: any; +} + +// Map of npm output levels to Cloud Logging levels. +// See https://github.com/winstonjs/winston#logging-levels for more info. +const NPM_LEVEL_NAME_TO_CODE = { + error: 3, + warn: 4, + info: 6, + http: 6, + verbose: 7, + debug: 7, + silly: 7, +}; + +// Map of Cloud Logging levels. +const CLOUD_LOGGING_LEVEL_CODE_TO_NAME: { + [key: number]: SeverityNames; +} = { + 0: 'emergency', + 1: 'alert', + 2: 'critical', + 3: 'error', + 4: 'warning', + 5: 'notice', + 6: 'info', + 7: 'debug', +}; + +/*! + * Log entry data key to allow users to indicate a trace for the request. + */ +export const LOGGING_TRACE_KEY = 'logging.googleapis.com/trace'; + +/*! + * Log entry data key to allow users to indicate a spanId for the request. + */ +export const LOGGING_SPAN_KEY = 'logging.googleapis.com/spanId'; + +/*! + * Log entry data key to allow users to indicate a traceSampled flag for the request. + */ +export const LOGGING_SAMPLED_KEY = 'logging.googleapis.com/trace_sampled'; + +/** + * Default library version to be used + * Using release-please annotations to update DEFAULT_INSTRUMENTATION_VERSION with latest version. + * See https://github.com/googleapis/release-please/blob/main/docs/customizing.md#updating-arbitrary-files + */ +export const NODEJS_WINSTON_DEFAULT_LIBRARY_VERSION = '6.0.1'; // {x-release-please-version} + +/*! + * Gets the current fully qualified trace ID when available from the + * @google-cloud/trace-agent library in the LogEntry.trace field format of: + * "projects/[PROJECT-ID]/traces/[TRACE-ID]". + */ +export function getCurrentTraceFromAgent(): string | null { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const agent = (global as any)._google_trace_agent; + if (!agent || !agent.getCurrentContextId || !agent.getWriterProjectId) { + return null; + } + + const traceId = agent.getCurrentContextId(); + if (!traceId) { + return null; + } + + const traceProjectId = agent.getWriterProjectId(); + if (!traceProjectId) { + return null; + } + + return `projects/${traceProjectId}/traces/${traceId}`; +} + +export class LoggingCommon { + readonly logName: string; + private inspectMetadata: boolean; + private levels: {[name: string]: number}; + cloudLog: LogSeverityFunctions; + private resource: protos.google.api.IMonitoredResource | undefined; + private serviceContext: ServiceContext | undefined; + private prefix: string | undefined; + private labels: object | undefined; + private defaultCallback?: Callback; + redirectToStdout: boolean; + // LOGGING_TRACE_KEY is Cloud Logging specific and has the format: + // logging.googleapis.com/trace + static readonly LOGGING_TRACE_KEY = LOGGING_TRACE_KEY; + // LOGGING_TRACE_KEY is Cloud Logging specific and has the format: + // logging.googleapis.com/spanId + static readonly LOGGING_SPAN_KEY = LOGGING_SPAN_KEY; + + constructor(options?: Options) { + options = Object.assign( + { + scopes: ['https://www.googleapis.com/auth/logging.write'], + }, + options, + ); + + this.logName = options.logName || 'winston_log'; + this.inspectMetadata = options.inspectMetadata === true; + this.levels = options.levels || NPM_LEVEL_NAME_TO_CODE; + this.redirectToStdout = options.redirectToStdout ?? false; + + if (!this.redirectToStdout) { + this.cloudLog = new Logging(options).log(this.logName, { + removeCircular: true, + // See: https://cloud.google.com/logging/quotas, a log size of + // 250,000 has been chosen to keep us comfortably within the + // 256,000 limit. + maxEntrySize: options.maxEntrySize || 250000, + }); + } else { + const logSyncOptions: LogSyncOptions = { + useMessageField: options.useMessageField ?? true, + }; + this.cloudLog = new Logging(options).logSync( + this.logName, + undefined, + logSyncOptions, + ); + } + this.resource = options.resource; + this.serviceContext = options.serviceContext; + this.prefix = options.prefix; + this.labels = options.labels; + this.defaultCallback = options.defaultCallback; + } + + log( + level: string, + message: string, + metadata: MetadataArg | undefined, + callback: Callback, + ) { + metadata = metadata || ({} as MetadataArg); + // First create instrumentation record if it is never written before + let instrumentationEntry: Entry | undefined; + if (!setInstrumentationStatus(true)) { + instrumentationEntry = createDiagnosticEntry( + 'nodejs-winston', + getNodejsLibraryVersion(), + ); + // Update instrumentation record resource, logName and timestamp + instrumentationEntry.metadata.resource = this.resource; + instrumentationEntry.metadata.logName = metadata.logName; + instrumentationEntry.metadata.timestamp = metadata.timestamp; + } + message = message || ''; + const hasMetadata = Object.keys(metadata).length; + + if (this.levels[level] === undefined) { + throw new Error('Unknown log level: ' + level); + } + + const levelCode = this.levels[level]; + const cloudLevel = CLOUD_LOGGING_LEVEL_CODE_TO_NAME[levelCode]; + + const data: StackdriverData = {}; + + // Cloud Logs Viewer picks up the summary line from the `message` + // property of the jsonPayload. + // https://cloud.google.com/logging/docs/view/logs_viewer_v2#expanding. + // + // For error messages at severity 'error' and higher, + // Error Reporting will pick up error messages if the full stack trace is + // included in the textPayload or the message property of the jsonPayload. + // https://cloud.google.com/error-reporting/docs/formatting-error-messages + // We prefer to format messages as jsonPayload (by putting it as a message + // property on an object) as that works and is accepted by Error Reporting + // in far more resource types. + // + if (metadata.stack) { + message += (message ? ' ' : '') + metadata.stack; + data.serviceContext = this.serviceContext; + } + + data.message = this.prefix ? `[${this.prefix}] ` : ''; + data.message += message; + + const entryMetadata: LogEntry = { + resource: this.resource, + }; + + // If the metadata contains a logName property, promote it to the entry + // metadata. + if (metadata.logName) { + entryMetadata.logName = metadata.logName; + } + + // If the metadata contains a httpRequest property, promote it to the + // entry metadata. This allows Cloud Logging to use request log formatting. + // https://cloud.google.com/logging/docs/reference/v2/rpc/google.logging.type#google.logging.type.HttpRequest + // Note that the httpRequest field must properly validate as HttpRequest + // proto message, or the log entry would be rejected by the API. We no do + // validation here. + if (metadata.httpRequest) { + entryMetadata.httpRequest = metadata.httpRequest; + } + + // If the metadata contains a timestamp property, promote it to the entry + // metadata. As Winston 3 buffers logs when a transport (such as this one) + // invokes its log callback asynchronously, a timestamp assigned at log time + // is more accurate than one assigned in a transport. + if (metadata.timestamp instanceof Date) { + entryMetadata.timestamp = metadata.timestamp; + } + + // If the metadata contains a labels property, promote it to the entry + // metadata. + // https://cloud.google.com/logging/docs/reference/v2/rpc/google.logging.v2#logentry + if (this.labels || metadata.labels) { + entryMetadata.labels = !this.labels + ? metadata.labels + : Object.assign({}, this.labels, metadata.labels); + } + + const trace = metadata[LOGGING_TRACE_KEY] || getCurrentTraceFromAgent(); + if (trace) { + entryMetadata.trace = trace as string; + } + + const spanId = metadata[LOGGING_SPAN_KEY]; + if (spanId) { + entryMetadata.spanId = spanId as string; + } + + if (LOGGING_SAMPLED_KEY in metadata) { + entryMetadata.traceSampled = metadata[LOGGING_SAMPLED_KEY] === true; + } + + // we have tests that assert that metadata is always passed. + // not sure if its correct but for now we always set it even if it has + // nothing in it + data.metadata = this.inspectMetadata + ? mapValues(metadata, util.inspect) + : metadata; + + if (hasMetadata) { + // clean entryMetadata props + delete data.metadata![LOGGING_TRACE_KEY]; + delete data.metadata![LOGGING_SPAN_KEY]; + delete data.metadata![LOGGING_SAMPLED_KEY]; + delete data.metadata!.httpRequest; + delete data.metadata!.labels; + delete data.metadata!.timestamp; + delete data.metadata!.logName; + } + + const entries: Entry[] = []; + entries.push(this.entry(entryMetadata, data)); + // Check if instrumentation entry needs to be added as well + if (instrumentationEntry) { + // Make sure instrumentation entry is updated by underlying logger + instrumentationEntry = this.entry( + instrumentationEntry.metadata, + instrumentationEntry.data, + ); + if (levelCode !== NPM_LEVEL_NAME_TO_CODE.info) { + // We using info level for diagnostic records + this.cloudLog[ + CLOUD_LOGGING_LEVEL_CODE_TO_NAME[NPM_LEVEL_NAME_TO_CODE.info] + ]([instrumentationEntry], this.defaultCallback); + } else entries.push(instrumentationEntry); + } + // Make sure that both callbacks are called in case if provided + const newCallback: Callback = (err: Error | null, apiResponse?: {}) => { + let callbackError: unknown; + if (callback) { + try { + callback(err, apiResponse); + } catch (error) { + callbackError = error; + } + } + if (this.defaultCallback) { + this.defaultCallback(err, apiResponse); + } + // In case if original error was null and callback thrown exception, rethrow it to make sure + // we do not swallow it since upon success the exceptions normally should not be thrown. Also + // we should retrhrow callbackError when defaultCallback was not provided to keep + // prevous behaviour intact + if ((!this.defaultCallback || err === null) && callbackError) { + throw callbackError; + } + }; + this.cloudLog[cloudLevel](entries, newCallback); + // The LogSync class does not supports callback. However Writable class always + // provides onwrite() callback which needs to be called after each log is written, + // so the stream would remove writing state. Since this.defaultCallback can also be set, we + // should call it explicitly as well. + if (this.redirectToStdout) { + newCallback(null, undefined); + } + } + + entry(metadata?: LogEntry, data?: string | {}): Entry { + if (this.redirectToStdout) { + return (this.cloudLog as LogSync).entry(metadata, data); + } + return (this.cloudLog as Log).entry(metadata, data); + } +} + +export function getNodejsLibraryVersion() { + return NODEJS_WINSTON_DEFAULT_LIBRARY_VERSION; +} + +type MetadataArg = { + stack?: {}; + /** + * set httpRequest to a http.clientRequest object to log it + */ + httpRequest?: protos.google.logging.type.IHttpRequest; + labels?: {}; + timestamp?: {}; + logName?: string; +} & {[key: string]: string | {}}; diff --git a/handwritten/nodejs-logging-winston/src/default-metadata.ts b/handwritten/nodejs-logging-winston/src/default-metadata.ts new file mode 100644 index 00000000000..6b14f3a64c3 --- /dev/null +++ b/handwritten/nodejs-logging-winston/src/default-metadata.ts @@ -0,0 +1,77 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {LOGGING_TRACE_KEY} from './common'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +declare const global: {[index: string]: any}; + +/** + * Returns an object that can be passed to Winston.createLogger as defaultMeta + * to allow log-trace correlation with Winston 3. Log-trace correlation with + * Winston 3 is broken because the trace ID to be correlated with a log isn't + * evaluated when the log function is called, but rather when the log is + * written, which happens at some future point where the trace ID may no longer + * be accurate. To circumvent this, we take advantage of the fact that + * defaultMeta is copied when a log function is called, and use a dynamic + * property getter to evaluate the trace ID upon that copy. + * + * We apply the same principle for timestamps, which is not strictly necessary + * for tracing but allows for more accurate timestamps in general. + * + * If there are other default metadata fields with which the return value of + * this function must be merged, this object MUST be the base object. In other + * words, do not use the return value of this function as the non-first argument + * to Object.assign, or it will not work. + * + * See https://github.com/googleapis/nodejs-logging-winston/issues/287 for + * more information. + */ +export function getDefaultMetadataForTracing() { + const agent = global._google_trace_agent; + // Enable log-trace correlation if the Trace Agent API is compatible. + const enableThunkAgent = !!( + agent && + agent.getCurrentContextId && + agent.getWriterProjectId + ); + + const defaultMeta = {}; + // Make defaultMeta.timestamp return the current timestamp any time it's + // accessed. + Object.defineProperty(defaultMeta, 'timestamp', { + enumerable: true, + get: () => new Date(), + }); + if (enableThunkAgent) { + // Make defaultMeta[LOGGING_TRACE_KEY] return the current trace ID any time + // it's accessed. + const loggingTraceKey = LOGGING_TRACE_KEY; + Object.defineProperty(defaultMeta, loggingTraceKey, { + enumerable: true, + get: () => { + const traceId = agent.getCurrentContextId(); + if (!traceId) { + return null; + } + const traceProjectId = agent.getWriterProjectId(); + if (!traceProjectId) { + return null; + } + return `projects/${traceProjectId}/traces/${traceId}`; + }, + }); + } + return defaultMeta; +} diff --git a/handwritten/nodejs-logging-winston/src/index.ts b/handwritten/nodejs-logging-winston/src/index.ts new file mode 100644 index 00000000000..8ca376338db --- /dev/null +++ b/handwritten/nodejs-logging-winston/src/index.ts @@ -0,0 +1,244 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import TransportStream = require('winston-transport'); + +import { + LOGGING_TRACE_KEY as COMMON_TRACE_KEY, + LOGGING_SPAN_KEY as COMMON_SPAN_KEY, + LOGGING_SAMPLED_KEY as COMMON_SAMPLED_KEY, + LoggingCommon, + getCurrentTraceFromAgent, +} from './common'; +import * as express from './middleware/express'; +import {getDefaultMetadataForTracing} from './default-metadata'; +import { + MonitoredResource, + ServiceContext, + LoggingOptions, +} from '@google-cloud/logging'; + +const LEVEL = Symbol.for('level'); + +// Export the express middleware as 'express'. +export {express}; +export {getDefaultMetadataForTracing}; +export {getCurrentTraceFromAgent}; + +type Callback = (err: Error | null, apiResponse?: {}) => void; + +export interface Options extends LoggingOptions { + /** + * The default log level. Winston will filter messages with a severity lower + * than this. + */ + level?: string; + /** + * Custom logging levels as supported by winston. This list is used to + * translate your log level to the Cloud Logging level. Each property + * should have an integer value between 0 (most severe) and 7 (least severe). + * If you are passing a list of levels to your winston logger, you should + * provide the same list here. + */ + levels?: {[name: string]: number}; + /** + * Serialize winston-provided log metadata using `util.inspect`. + */ + inspectMetadata?: boolean; + /** + * The name of the log that will receive messages written to this transport. + */ + logName?: string; + /** + * The monitored resource that the transport corresponds to. On Google Cloud + * Platform, this is detected automatically, but you may optionally specify a + * specific monitored resource. For more information see the + * [official documentation]{@link + * https://cloud.google.com/logging/docs/api/reference/rest/v2/MonitoredResource}. + */ + resource?: MonitoredResource; + /** + * For logged errors, we provide this as the service context. For more + * information see [this guide]{@link + * https://cloud.google.com/error-reporting/docs/formatting-error-messages} + * and the [official documentation]{@link + * https://cloud.google.com/error-reporting/reference/rest/v1beta1/ServiceContext}. + */ + serviceContext?: ServiceContext; + + logname?: string; + + prefix?: string; + + labels?: {[key: string]: string}; + + // An attempt will be made to truncate messages larger than maxEntrySize. + // Please note that this parameter is ignored when redirectToStdout is set. + maxEntrySize?: number; + + // A default global callback to be used for {@link LoggingWinston#log} when callback is + // not supplied by caller in function parameters + defaultCallback?: Callback; + + /** + * Boolen flag that opts-in redirecting the output to STDOUT instead of ingesting logs to Cloud + * Logging using Logging API. Defaults to {@code false}. Redirecting logs can be used in + * Google Cloud environments with installed logging agent to delegate log ingestions to the + * agent. Redirected logs are formatted as one line Json string following the structured logging guidelines. + */ + redirectToStdout?: boolean; + + /** + * Boolean flag indicating if "message" field should be used to store structured, + * non-text data inside jsonPayload field. This flag applies only when {@link Options#redirectToStdout} is set. + * By default this value is true + */ + useMessageField?: boolean; + + /** + * Additional parameters for {@link TransportStream}. For more information on parameters, + * please see [winston-transport](https://github.com/winstonjs/winston-transport/blob/0e5e4c0056188a74e24407ee066902fb113bd8de/index.js#L8). + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + format?: any; + silent?: boolean; + handleExceptions?: boolean; + handleRejections?: boolean; +} + +/** + * This module provides support for streaming your winston logs to + * [Cloud Logging](https://cloud.google.com/logging). + * + * @class + * + * @param {object} [options] + * @param {object} [options.level] The default log level. Winston will filter + * messages with a severity lower than this. + * @param {object} [options.levels] Custom logging levels as supported by + * winston. This list is used to translate your log level to the Cloud + * Logging level. Each property should have an integer value between 0 (most + * severe) and 7 (least severe). If you are passing a list of levels to your + * winston logger, you should provide the same list here. + * @param {boolean} [options.inspectMetadata=false] Serialize winston-provided log + * metadata using `util.inspect`. + * @param {string} [options.logName=winston_log] The name of the log that will receive + * messages written to this transport. + * @param {object} [options.resource] The monitored resource that the transport + * corresponds to. On Google Cloud Platform, this is detected automatically, + * but you may optionally specify a specific monitored resource. For more + * information see the + * [official documentation]{@link + * https://cloud.google.com/logging/docs/api/reference/rest/v2/MonitoredResource}. + * @param {object} [options.serviceContext] For logged errors, we provide this + * as the service context. For more information see + * [this guide]{@link + * https://cloud.google.com/error-reporting/docs/formatting-error-messages} and + * the [official documentation]{@link + * https://cloud.google.com/error-reporting/reference/rest/v1beta1/ServiceContext}. + * @param {string} [options.serviceContext.service] An identifier of the + * service, such as the name of the executable, job, or Google App Engine + * service name. + * @param {string} [options.serviceContext.version] Represents the version of + * the service. + * @param {string} [options.projectId] The project ID from the Google Cloud + * Console, e.g. 'grape-spaceship-123'. We will also check the environment + * variable `GCLOUD_PROJECT` for your project ID. If your app is running in + * an environment which supports {@link + * https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application + * Application Default Credentials}, your project ID will be detected + * automatically. + * @param {string} [options.keyFilename] Full path to the a .json, .pem, or .p12 + * key downloaded from the Google Cloud Console. If you provide a path + * to a JSON file, the `projectId` option above is not necessary. NOTE: .pem + * and .p12 require you to specify the `email` option as well. + * @param {string} [options.email] Account email address. Required when using a + * .pem or .p12 keyFilename. + * @param {object} [options.credentials] Credentials object. + * @param {string} [options.credentials.client_email] + * @param {string} [options.credentials.private_key] + * @param {boolean} [options.autoRetry=true] Automatically retry requests if the + * response is related to rate limits or certain intermittent server errors. + * We will exponentially backoff subsequent requests by default. + * @param {number} [options.maxRetries=3] Maximum number of automatic retries + * attempted before returning the error. + * @param {constructor} [options.promise] Custom promise module to use instead + * of native Promises. + * + * @example Import the client library + * const {LoggingWinston} = require('@google-cloud/logging-winston'); + * + * @example Create a client that uses Application + * Default Credentials (ADC): const loggingWinston = new + * LoggingWinston(); + * + * @example Create a client with explicit + * credentials: const loggingWinston = new LoggingWinston({ + * projectId: 'your-project-id', + * keyFilename: '/path/to/keyfile.json' + * }); + * + * @example include:samples/quickstart.js + * region_tag:logging_winston_quickstart + * Full quickstart example: + */ +export class LoggingWinston extends TransportStream { + static readonly LOGGING_TRACE_KEY = COMMON_TRACE_KEY; + static readonly LOGGING_SPAN_KEY = COMMON_SPAN_KEY; + static readonly LOGGING_SAMPLED_KEY = COMMON_SAMPLED_KEY; + + common: LoggingCommon; + + constructor(options?: Options) { + options = options || {}; + super({ + level: options.level, + format: options.format, + silent: options.silent, + handleExceptions: options.handleExceptions, + handleRejections: options.handleRejections, + }); + this.common = new LoggingCommon(options); + } + + // eslint-disable-next-line + log(info: any, callback: Callback) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const {message, level, splat, stack, ...metadata} = info; + + // If the whole message is an error we have to manually copy the stack into + // metadata. Errors dont have enumerable properties so they don't + // destructure. + if (stack) metadata.stack = stack; + this.common.log(info[LEVEL] || level, message, metadata || {}, callback); + } +} + +// LOGGING_TRACE_KEY is Cloud Logging specific and has the format: +// logging.googleapis.com/trace +// For more information, see: https://cloud.google.com/logging/docs/reference/v2/rpc/google.logging.v2#logentry +export const LOGGING_TRACE_KEY = COMMON_TRACE_KEY; + +// LOGGING_SPAN_KEY is Cloud Logging specific and has the format: +// logging.googleapis.com/spanId +// For more information, see: https://cloud.google.com/logging/docs/reference/v2/rpc/google.logging.v2#logentry +export const LOGGING_SPAN_KEY = COMMON_SPAN_KEY; + +// LOGGING_SAMPLED_KEY is Cloud Logging specific and has the format: +// logging.googleapis.com/trace_sampled +// The value of this field must be either true or false. For more information, +// see traceSampled on the LogEntry page: https://cloud.google.com/logging/docs/reference/v2/rpc/google.logging.v2#logentry +export const LOGGING_SAMPLED_KEY = COMMON_SAMPLED_KEY; diff --git a/handwritten/nodejs-logging-winston/src/middleware/express.ts b/handwritten/nodejs-logging-winston/src/middleware/express.ts new file mode 100644 index 00000000000..2cfc5594d9f --- /dev/null +++ b/handwritten/nodejs-logging-winston/src/middleware/express.ts @@ -0,0 +1,124 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { + HttpRequest, + Log, + LogSync, + middleware as commonMiddleware, +} from '@google-cloud/logging'; +import {GCPEnv} from 'google-auth-library'; +import * as winston from 'winston'; + +import { + LOGGING_TRACE_KEY, + LOGGING_SPAN_KEY, + LOGGING_SAMPLED_KEY, +} from '../common'; +import {LoggingWinston, Options} from '../index'; +import {makeChildLogger} from './make-child-logger'; + +export const REQUEST_LOG_SUFFIX = '_reqlog'; + +type Middleware = ReturnType; + +export async function makeMiddleware( + logger: winston.Logger, + transport: LoggingWinston, + skipParentEntryForCloudRun?: boolean, +): Promise; +export async function makeMiddleware( + logger: winston.Logger, + options?: Options, + skipParentEntryForCloudRun?: boolean, +): Promise; +export async function makeMiddleware( + logger: winston.Logger, + optionsOrTransport?: Options | LoggingWinston, + skipParentEntryForCloudRun?: boolean, +): Promise { + let transport: LoggingWinston; + + // If no custom transports are provided, use default or instantiate one. + const cloudTransport = logger.transports.find( + t => t instanceof LoggingWinston, + ); + + // If user provides a custom transport, always add it to the logger. + if (optionsOrTransport instanceof LoggingWinston) { + transport = optionsOrTransport; + logger.add(transport); + } else if (cloudTransport && !optionsOrTransport) { + // Check if logger already contains a Cloud transport + transport = cloudTransport as LoggingWinston; + } else { + const options = {logName: 'winston_log', ...optionsOrTransport}; + transport = new LoggingWinston(options); + logger.add(transport); + } + + const auth = ( + transport.common.redirectToStdout + ? (transport.common.cloudLog as LogSync) + : (transport.common.cloudLog as Log) + ).logging.auth; + const [env, projectId] = await Promise.all([ + auth.getEnv(), + auth.getProjectId(), + ]); + + // Unless we are running on Google App Engine or Cloud Functions, generate a + // parent request log entry that all the request specific logs ("app logs") + // will nest under. GAE and GCF generate the parent request logs + // automatically. + // Cloud Run also generates the parent request log automatically, but skipping + // the parent request entry has to be explicity enabled until the next major + // release in which we can change the default behavior. + let emitRequestLogEntry; + if ( + env !== GCPEnv.APP_ENGINE && + env !== GCPEnv.CLOUD_FUNCTIONS && + (env !== GCPEnv.CLOUD_RUN || !skipParentEntryForCloudRun) + ) { + const requestLogName = Log.formatName_( + projectId, + `${transport.common.logName}${REQUEST_LOG_SUFFIX}`, + ); + + emitRequestLogEntry = ( + httpRequest: HttpRequest, + trace: string, + span?: string, + sampled?: boolean, + ) => { + logger.info({ + // The request logs must have a log name distinct from the app logs + // for log correlation to work. + logName: requestLogName, + [LOGGING_TRACE_KEY]: trace, + [LOGGING_SPAN_KEY]: span, + [LOGGING_SAMPLED_KEY]: sampled, + httpRequest, + message: httpRequest.requestUrl || 'http request', + }); + }; + } + + return commonMiddleware.express.makeMiddleware( + projectId, + (trace: string, span?: string, sampled?: boolean) => + makeChildLogger(logger, trace, span, sampled), + emitRequestLogEntry, + ); +} diff --git a/handwritten/nodejs-logging-winston/src/middleware/make-child-logger.ts b/handwritten/nodejs-logging-winston/src/middleware/make-child-logger.ts new file mode 100644 index 00000000000..45250ee33ea --- /dev/null +++ b/handwritten/nodejs-logging-winston/src/middleware/make-child-logger.ts @@ -0,0 +1,33 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as winston from 'winston'; +import { + LOGGING_TRACE_KEY, + LOGGING_SPAN_KEY, + LOGGING_SAMPLED_KEY, +} from '../index'; + +export function makeChildLogger( + logger: winston.Logger, + trace: string, + span?: string, + sampled?: boolean, +) { + return logger.child({ + [LOGGING_TRACE_KEY]: trace, + [LOGGING_SPAN_KEY]: span, + [LOGGING_SAMPLED_KEY]: sampled, + }); +} diff --git a/handwritten/nodejs-logging-winston/system-test/errors-transport.ts b/handwritten/nodejs-logging-winston/system-test/errors-transport.ts new file mode 100644 index 00000000000..2b96c6b3147 --- /dev/null +++ b/handwritten/nodejs-logging-winston/system-test/errors-transport.ts @@ -0,0 +1,138 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as common from '@google-cloud/common'; + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const packageJson = require('../../package.json'); + +export interface ServiceContext { + service: string; + version: string; + resourceType: string; +} + +export interface ErrorEvent { + eventTime: string; + serviceContext: ServiceContext; + message: string; + // other fields not used in the tests have been omitted +} + +export interface ErrorGroupStats { + group: {groupId: string}; + affectedServices: ServiceContext[]; + representative: ErrorEvent; + count: string; + // other fields not used in the tests have been omitted +} + +export interface ApiResponse { + body: {errorGroupStats: ErrorGroupStats[]; errorEvents: ErrorEvent[]}; +} + +/* @const {String} Base Error Reporting API */ +const API = 'https://clouderrorreporting.googleapis.com/v1beta1/projects'; + +const ONE_HOUR_API = 'timeRange.period=PERIOD_1_HOUR'; + +function delay(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +export class ErrorsApiTransport extends common.Service { + constructor() { + super({ + baseUrl: 'https://clouderrorreporting.googleapis.com/v1beta1', + apiEndpoint: 'clouderrorreporting.googleapis.com', + scopes: ['https://www.googleapis.com/auth/cloud-platform'], + packageJson, + }); + } + + async request(options: common.DecorateRequestOptions) { + return new Promise((resolve, reject) => { + super.request(options, (err, _, res) => + err ? reject(err) : resolve(res as common.ResponseBody), + ); + }); + } + + async getAllGroups(): Promise { + const projectId = await this.getProjectId(); + const options = { + uri: [API, projectId, 'groupStats?' + ONE_HOUR_API].join('/'), + method: 'GET', + }; + + const response = await this.request(options); + return response.body.errorGroupStats || []; + } + + async getGroupEvents(groupId: string): Promise { + const projectId = await this.getProjectId(); + const options = { + uri: [ + API, + projectId, + 'events?groupId=' + groupId + '&pageSize=10&' + ONE_HOUR_API, + ].join('/'), + method: 'GET', + }; + + const response = await this.request(options); + return response.body.errorEvents || []; + } + + async pollForNewEvents( + service: string, + time: number, + timeout: number, + ): Promise { + const timeLimit = Date.now() + timeout; + let groupId; + // wait for a group + while (Date.now() < timeLimit) { + await delay(1000); + + if (!groupId) { + const groups = await this.getAllGroups(); + if (!groups.length) continue; + // find an error group that matches the service + groups.forEach(group => { + const match = group.affectedServices.find( + context => context.service === service, + ); + if (match) { + groupId = group.group.groupId; + } + }); + } + + // didnt find an error reporting group matching the service. + if (!groupId) continue; + + const events = await this.getGroupEvents(groupId); + const filteredEvents = events.filter( + event => + event.serviceContext.service === service && + new Date(event.eventTime).getTime() >= time, + ); + if (filteredEvents.length) { + return filteredEvents; + } + } + return []; + } +} diff --git a/handwritten/nodejs-logging-winston/system-test/test-install.ts b/handwritten/nodejs-logging-winston/system-test/test-install.ts new file mode 100644 index 00000000000..73db55e3639 --- /dev/null +++ b/handwritten/nodejs-logging-winston/system-test/test-install.ts @@ -0,0 +1,159 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {packNTest} from 'pack-n-play'; +import {describe, it} from 'mocha'; + +describe('pack-n-play', () => { + const TS_CODE_SAMPLES = [ + { + ts: `import * as loggingWinston from '@google-cloud/logging-winston'; +new loggingWinston.LoggingWinston();`, + description: 'imports the module using * syntax', + dependencies: ['winston'], + devDependencies: ['@types/winston', 'typescript@5'], + }, + { + ts: `import {LoggingWinston} from '@google-cloud/logging-winston'; +new LoggingWinston();`, + description: 'imports the module with {} syntax', + dependencies: ['winston'], + devDependencies: ['@types/winston', 'typescript@5'], + }, + { + ts: `import {LoggingWinston} from '@google-cloud/logging-winston'; +new LoggingWinston({ + serviceContext: { + service: 'some service' + } +});`, + description: + 'imports the module and starts with a partial `serviceContext`', + dependencies: ['winston'], + devDependencies: ['@types/winston', 'typescript@5'], + }, + { + ts: `import {LoggingWinston} from '@google-cloud/logging-winston'; +new LoggingWinston({ + projectId: 'some-project', + serviceContext: { + service: 'Some service', + version: 'Some version' + } +});`, + description: + 'imports the module and starts with a complete `serviceContext`', + dependencies: ['winston'], + devDependencies: ['@types/winston', 'typescript@5'], + }, + + { + ts: `import {LoggingWinston} from '@google-cloud/logging-winston'; + import * as winston from 'winston'; +const loggingWinston = new LoggingWinston({ + prefix: 'some-prefix', + labels: { + env: 'local', + name: 'some-name', + version: 'some-version' + } +}); + +winston.createLogger({transports:[loggingWinston]}) +`, + description: 'imports the module with a prefix and labels specified', + dependencies: ['winston'], + devDependencies: ['typescript@5'], + }, + { + ts: `import { LoggingWinston } from '@google-cloud/logging-winston'; + import * as winston from 'winston'; + + winston.createLogger({ + transports: [ + new LoggingWinston(), + ], + });`, + description: 'imports transport-stream correctly', + dependencies: ['winston', 'winston-transport'], + devDependencies: [], + }, + ]; + + const JS_CODE_SAMPLES = [ + { + js: `const LoggingWinston = require('@google-cloud/logging-winston').LoggingWinston; +new LoggingWinston();`, + description: 'requires the module using Node 4+ syntax', + dependencies: ['winston'], + devDependencies: [], + }, + { + js: `const LoggingWinston = require('@google-cloud/logging-winston').LoggingWinston; +new LoggingWinston({ + serviceContext: { + service: 'some service' + } +});`, + description: + 'requires the module and starts with a partial `serviceContext`', + dependencies: ['winston'], + devDependencies: [], + }, + { + js: `const LoggingWinston = require('@google-cloud/logging-winston').LoggingWinston; +new LoggingWinston({ + projectId: 'some-project', + serviceContext: { + service: 'Some service', + version: 'Some version' + } +});`, + description: + 'requires the module and starts with a complete `serviceContext`', + dependencies: ['winston'], + devDependencies: [], + }, + { + js: `const LoggingWinston = require('@google-cloud/logging-winston').LoggingWinston; +new LoggingWinston({ + prefix: 'some-prefix', + labels: { + env: 'local', + name: 'some-name', + version: 'some-version' + } +});`, + description: 'imports the module with a prefix and labels specified', + dependencies: ['winston'], + devDependencies: [], + }, + ]; + + TS_CODE_SAMPLES.forEach(sample => { + it(sample.description, async () => { + await packNTest({ + sample, + }); + }).timeout(2 * 60 * 1000); + }); + + JS_CODE_SAMPLES.forEach(sample => { + it(sample.description, async () => { + await packNTest({ + sample, + }); + }).timeout(2 * 60 * 1000); + }); +}); diff --git a/handwritten/nodejs-logging-winston/system-test/test-middleware-express.ts b/handwritten/nodejs-logging-winston/system-test/test-middleware-express.ts new file mode 100644 index 00000000000..c813b6d1ac4 --- /dev/null +++ b/handwritten/nodejs-logging-winston/system-test/test-middleware-express.ts @@ -0,0 +1,126 @@ +/*! + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as assert from 'assert'; +import {describe, it} from 'mocha'; +import * as crypto from 'crypto'; +import {express as elb} from '../src/index'; +import * as winston from 'winston'; +import {REQUEST_LOG_SUFFIX} from '../src/middleware/express'; + +import {Logging} from '@google-cloud/logging'; +const logging = new Logging(); + +const WRITE_CONSISTENCY_DELAY_MS = 20 * 1000; +const TEST_TIMEOUT = WRITE_CONSISTENCY_DELAY_MS + 10 * 1000; +const LOG_NAME = `winston-system-test-${crypto.randomBytes(16).toString('hex')}`; + +function delay(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +describe(__filename, () => { + describe('global logger', () => { + it('should properly write log entries', async function () { + this.timeout(TEST_TIMEOUT); + const logger = winston.createLogger(); + await elb.makeMiddleware(logger, { + logName: LOG_NAME, + level: 'info', + }); + + const LOG_MESSAGE = `unique log message ${crypto.randomBytes(16).toString('hex')}`; + logger.info(LOG_MESSAGE); + + await delay(WRITE_CONSISTENCY_DELAY_MS); + + const log = logging.log(LOG_NAME); + const entries = (await log.getEntries({pageSize: 1}))[0]; + assert.strictEqual(entries.length, 1); + assert.strictEqual(LOG_MESSAGE, entries[0].data.message); + }); + }); + + describe('request logging middleware', () => { + it('should write request correlated log entries', function () { + this.timeout(TEST_TIMEOUT); + // eslint-disable-next-line no-async-promise-executor + return new Promise(async resolve => { + const logger = winston.createLogger(); + const mw = await elb.makeMiddleware(logger, { + logName: LOG_NAME, + level: 'info', + }); + + const LOG_MESSAGE = `correlated log message ${crypto.randomBytes(16).toString('hex')}`; + const fakeRequest = { + headers: { + 'user-agent': 'Mocha/test-case', + }, + statusCode: 200, + originalUrl: 'http://google.com', + method: 'PUSH', + }; + const fakeResponse = { + getHeader: (name: string) => { + return name === 'Content-Length' + ? 4104 + : `header-value-for-${name}`; + }, + }; + + const next = async () => { + // At this point fakeRequest.log should have been installed. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (fakeRequest as any).log.info(LOG_MESSAGE); + + await delay(WRITE_CONSISTENCY_DELAY_MS); + + const appLog = logging.log(LOG_NAME); + const appLogEntries = (await appLog.getEntries({pageSize: 1}))[0]; + assert.strictEqual(appLogEntries.length, 1); + const [appLogEntry] = appLogEntries; + assert.strictEqual(LOG_MESSAGE, appLogEntry.data.message); + assert(appLogEntry.metadata.trace, 'should have a trace property'); + assert(appLogEntry.metadata.trace!.match(/projects\/.*\/traces\/.*/)); + assert(appLogEntry.metadata.spanId, 'should have a span property'); + assert(appLogEntry.metadata.spanId!.match(/^[0-9]*/)); + assert.strictEqual(appLogEntry.metadata.traceSampled, false); + assert.strictEqual(appLogEntry.metadata.severity, 'INFO'); + + const requestLog = logging.log(`${LOG_NAME}${REQUEST_LOG_SUFFIX}`); + const requestLogEntries = ( + await requestLog.getEntries({ + pageSize: 1, + }) + )[0]; + assert.strictEqual(requestLogEntries.length, 1); + const [requestLogEntry] = requestLogEntries; + assert.strictEqual( + requestLogEntry.metadata.trace, + appLogEntry.metadata.trace, + ); + + resolve(); + }; + + // Call middleware with mocks. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + mw(fakeRequest as any, fakeResponse as any, next); + }); + }); + }); +}); diff --git a/handwritten/nodejs-logging-winston/test/common.ts b/handwritten/nodejs-logging-winston/test/common.ts new file mode 100644 index 00000000000..36b4b3ea396 --- /dev/null +++ b/handwritten/nodejs-logging-winston/test/common.ts @@ -0,0 +1,636 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as assert from 'assert'; +import {describe, it, beforeEach} from 'mocha'; +import * as nodeutil from 'util'; +import * as proxyquire from 'proxyquire'; +import {Options} from '../src'; +import {Entry, Logging, LogSync, Log} from '@google-cloud/logging'; +import * as instrumentation from '@google-cloud/logging/build/src/utils/instrumentation'; +import {LoggingCommon} from '../src/common'; + +declare const global: {[index: string]: {} | null}; + +interface Metadata { + value(): void; + labels?: {label2?: string}; +} + +describe('logging-common', () => { + let fakeLogInstance: Logging; + let fakeLoggingOptions_: Options | null; + let fakeLogName_: string | null; + let fakeLogOptions_: object | null; + + function fakeLogging(options: Options) { + fakeLoggingOptions_ = options; + return { + log: (logName: string, logOptions: object) => { + fakeLogName_ = logName; + fakeLogOptions_ = logOptions; + return fakeLogInstance; + }, + }; + } + + class FakeTransport { + // transportCalledWith_ takes arguments which cannot be determined type. + transportCalledWith_: Array<{}>; + constructor(...args: Array<{}>) { + this.transportCalledWith_ = args; + } + } + + const fakeWinston = { + transports: {}, + Transport: FakeTransport, + }; + + const loggingCommonLib = proxyquire('../src/common', { + '@google-cloud/logging': { + Logging: fakeLogging, + }, + winston: fakeWinston, + }); + + // loggingCommon is loggingCommon namespace which cannot be determined type. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let loggingCommon: any; + + const OPTIONS: Options = { + logName: 'log-name', + levels: { + one: 1, + six: 6, + }, + resource: {}, + serviceContext: { + service: 'fake-service', + }, + }; + + beforeEach(() => { + fakeLogInstance = {} as unknown as Logging; + fakeLoggingOptions_ = null; + fakeLogName_ = null; + loggingCommon = new loggingCommonLib.LoggingCommon(OPTIONS); + }); + + describe('instantiation', () => { + it('should default to logging.write scope', () => { + assert.deepStrictEqual((fakeLoggingOptions_ as Options).scopes, [ + 'https://www.googleapis.com/auth/logging.write', + ]); + }); + + it('should initialize Log instance using provided scopes', () => { + const fakeScope = 'fake scope'; + + const optionsWithScopes: Options = Object.assign({}, OPTIONS); + optionsWithScopes.scopes = fakeScope; + + new loggingCommonLib.LoggingCommon(optionsWithScopes); + + assert.deepStrictEqual(fakeLoggingOptions_, optionsWithScopes); + }); + + it('should localize inspectMetadata to default value', () => { + assert.strictEqual(loggingCommon.inspectMetadata, false); + }); + + it('should localize the provided options.inspectMetadata', () => { + const optionsWithInspectMetadata = Object.assign({}, OPTIONS, { + inspectMetadata: true, + }); + + const loggingCommon = new loggingCommonLib.LoggingCommon( + optionsWithInspectMetadata, + ); + assert.strictEqual(loggingCommon.inspectMetadata, true); + }); + + it('should localize provided levels', () => { + assert.strictEqual(loggingCommon.levels, OPTIONS.levels); + }); + + it('should default to npm levels', () => { + const optionsWithoutLevels = Object.assign({}, OPTIONS); + delete optionsWithoutLevels.levels; + + const loggingCommon = new loggingCommonLib.LoggingCommon( + optionsWithoutLevels, + ); + assert.deepStrictEqual(loggingCommon.levels, { + error: 3, + warn: 4, + info: 6, + http: 6, + verbose: 7, + debug: 7, + silly: 7, + }); + }); + + it('should localize Log instance using default name', () => { + const logName = 'log-name-override'; + + const optionsWithLogName = Object.assign({}, OPTIONS); + optionsWithLogName.logName = logName; + + const loggingCommon = new loggingCommonLib.LoggingCommon( + optionsWithLogName, + ); + + const loggingOptions = Object.assign({}, fakeLoggingOptions_); + delete (loggingOptions as Options).scopes; + + assert.deepStrictEqual(loggingOptions, optionsWithLogName); + assert.strictEqual(fakeLogName_, logName); + assert.strictEqual(loggingCommon.logName, logName); + }); + + it('should set removeCircular to true', () => { + new loggingCommonLib.LoggingCommon(OPTIONS); + + assert.deepStrictEqual(fakeLogOptions_, { + removeCircular: true, + maxEntrySize: 250000, + }); + }); + + it('should localize the provided resource', () => { + assert.strictEqual(loggingCommon.resource, OPTIONS.resource); + }); + + it('should localize the provided service context', () => { + assert.strictEqual(loggingCommon.serviceContext, OPTIONS.serviceContext); + }); + + it('should create LogCommon with LogSync', () => { + const optionsWithRedirectToStdout = Object.assign({}, OPTIONS, { + redirectToStdout: true, + }); + const loggingCommon = new LoggingCommon(optionsWithRedirectToStdout); + assert.ok(loggingCommon.cloudLog instanceof LogSync); + }); + + it('should create LogCommon with LogSync and useMessage is on', () => { + const optionsWithRedirectToStdoutAndUseMessage = Object.assign( + {}, + OPTIONS, + { + redirectToStdout: true, + useMessageField: true, + }, + ); + const loggingCommon = new LoggingCommon( + optionsWithRedirectToStdoutAndUseMessage, + ); + assert.ok(loggingCommon.cloudLog instanceof LogSync); + assert.ok(loggingCommon.cloudLog.useMessageField_ === true); + }); + + it('should create LogCommon with Log', () => { + const loggingCommon = new LoggingCommon(OPTIONS); + assert.ok(loggingCommon.cloudLog instanceof Log); + }); + }); + + describe('log', () => { + const LEVEL = Object.keys(OPTIONS.levels as {[name: string]: number})[0]; + const INFO = Object.keys(OPTIONS.levels as {[name: string]: number})[1]; + const STACKDRIVER_LEVEL = 'alert'; // (code 1) + const MESSAGE = 'message'; + const METADATA = { + value: () => {}, + }; + + beforeEach(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + fakeLogInstance.entry = (() => {}) as any; + loggingCommon.cloudLog.emergency = () => {}; + loggingCommon.cloudLog[STACKDRIVER_LEVEL] = () => {}; + }); + + it('should throw on a bad log level', () => { + assert.throws(() => { + loggingCommon.log( + 'non-existent-level', + MESSAGE, + METADATA, + assert.ifError, + ); + }, /Unknown log level: non-existent-level/); + }); + + it('should not throw on `0` log level', () => { + const options = Object.assign({}, OPTIONS, { + levels: { + zero: 0, + }, + }); + + loggingCommon = new loggingCommonLib.LoggingCommon(options); + + loggingCommon.log('zero', 'test message'); + }); + + it('should properly create an entry', done => { + loggingCommon.cloudLog.entry = (entryMetadata: {}, data: {}) => { + assert.deepStrictEqual(entryMetadata, { + resource: loggingCommon.resource, + }); + assert.deepStrictEqual(data, { + message: MESSAGE, + metadata: METADATA, + }); + done(); + }; + + loggingCommon.log(LEVEL, MESSAGE, METADATA, assert.ifError); + }); + + it('should append stack when metadata is an error', done => { + const error = { + stack: 'the stack', + }; + + loggingCommon.cloudLog.entry = (entryMetadata: {}, data: {}) => { + assert.deepStrictEqual(data, { + message: MESSAGE + ' ' + error.stack, + metadata: error, + serviceContext: OPTIONS.serviceContext, + }); + done(); + }; + + loggingCommon.log(LEVEL, MESSAGE, error, assert.ifError); + }); + + it('should use stack when metadata is err without message', done => { + const error = { + stack: 'the stack', + }; + + loggingCommon.cloudLog.entry = (entryMetadata: {}, data: {}) => { + assert.deepStrictEqual(data, { + message: error.stack, + metadata: error, + serviceContext: OPTIONS.serviceContext, + }); + done(); + }; + + loggingCommon.log(LEVEL, '', error, assert.ifError); + }); + + it('should inspect metadata when inspectMetadata is set', done => { + loggingCommon.inspectMetadata = true; + + loggingCommon.cloudLog.entry = (_: {}, data: {}) => { + const expectedWinstonMetadata = {}; + + for (const prop of Object.keys(METADATA)) { + // metadata does not have index signature. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (expectedWinstonMetadata as any)[prop] = + // eslint-disable-next-line @typescript-eslint/no-explicit-any + nodeutil.inspect((METADATA as any)[prop]); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + assert.deepStrictEqual((data as any).metadata, expectedWinstonMetadata); + + done(); + }; + + loggingCommon.log(LEVEL, MESSAGE, METADATA, assert.ifError); + }); + + it('should promote httpRequest property to metadata', done => { + const HTTP_REQUEST = { + statusCode: 418, + }; + const metadataWithRequest = Object.assign( + { + httpRequest: HTTP_REQUEST, + }, + METADATA, + ); + + loggingCommon.cloudLog.entry = (entryMetadata: {}, data: {}) => { + assert.deepStrictEqual(entryMetadata, { + resource: loggingCommon.resource, + httpRequest: HTTP_REQUEST, + }); + assert.deepStrictEqual(data, { + message: MESSAGE, + metadata: METADATA, + }); + done(); + }; + loggingCommon.log(LEVEL, MESSAGE, metadataWithRequest, assert.ifError); + }); + + it('should promote timestamp property to metadata', done => { + const date = new Date(); + const metadataWithRequest = Object.assign( + { + timestamp: date, + }, + METADATA, + ); + + loggingCommon.cloudLog.entry = (entryMetadata: {}, data: {}) => { + assert.deepStrictEqual(entryMetadata, { + resource: loggingCommon.resource, + timestamp: date, + }); + assert.deepStrictEqual(data, { + message: MESSAGE, + metadata: METADATA, + }); + done(); + }; + loggingCommon.log(LEVEL, MESSAGE, metadataWithRequest, assert.ifError); + }); + + it('should promote labels from metadata to log entry', done => { + const LABELS = {labelKey: 'labelValue'}; + const metadataWithLabels = Object.assign({labels: LABELS}, METADATA); + + loggingCommon.cloudLog.entry = (entryMetadata: {}, data: {}) => { + assert.deepStrictEqual(entryMetadata, { + resource: loggingCommon.resource, + labels: LABELS, + }); + assert.deepStrictEqual(data, { + message: MESSAGE, + metadata: METADATA, + }); + done(); + }; + loggingCommon.log(LEVEL, MESSAGE, metadataWithLabels, assert.ifError); + }); + + it('should promote prefixed trace properties to metadata', done => { + const metadataWithTrace = Object.assign({}, METADATA); + const loggingTraceKey = loggingCommonLib.LOGGING_TRACE_KEY; + const loggingSpanKey = loggingCommonLib.LOGGING_SPAN_KEY; + const loggingSampledKey = loggingCommonLib.LOGGING_SAMPLED_KEY; + // metadataWithTrace does not have index signature. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (metadataWithTrace as any)[loggingTraceKey] = 'trace1'; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (metadataWithTrace as any)[loggingSpanKey] = 'span1'; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (metadataWithTrace as any)[loggingSampledKey] = true; + + loggingCommon.cloudLog.entry = (entryMetadata: {}, data: {}) => { + assert.deepStrictEqual(entryMetadata, { + resource: loggingCommon.resource, + trace: 'trace1', + spanId: 'span1', + traceSampled: true, + }); + assert.deepStrictEqual(data, { + message: MESSAGE, + metadata: METADATA, + }); + done(); + }; + loggingCommon.log(LEVEL, MESSAGE, metadataWithTrace, assert.ifError); + }); + + it('should promote a false traceSampled value to metadata', done => { + const metadataWithTrace = Object.assign({}, METADATA); + const loggingSampledKey = loggingCommonLib.LOGGING_SAMPLED_KEY; + // metadataWithTrace does not have index signature. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (metadataWithTrace as any)[loggingSampledKey] = '0'; + + loggingCommon.cloudLog.entry = (entryMetadata: {}, data: {}) => { + assert.deepStrictEqual(entryMetadata, { + resource: loggingCommon.resource, + traceSampled: false, + }); + assert.deepStrictEqual(data, { + message: MESSAGE, + metadata: METADATA, + }); + done(); + }; + loggingCommon.log(LEVEL, MESSAGE, metadataWithTrace, assert.ifError); + }); + + it('should set trace metadata from agent if available', done => { + const oldTraceAgent = global._google_trace_agent; + global._google_trace_agent = { + getCurrentContextId: () => { + return 'trace1'; + }, + getWriterProjectId: () => { + return 'project1'; + }, + }; + loggingCommon.cloudLog.entry = (entryMetadata: {}, data: {}) => { + assert.deepStrictEqual(entryMetadata, { + resource: loggingCommon.resource, + trace: 'projects/project1/traces/trace1', + }); + assert.deepStrictEqual(data, { + message: MESSAGE, + metadata: METADATA, + }); + done(); + }; + + loggingCommon.log(LEVEL, MESSAGE, METADATA, assert.ifError); + + global._google_trace_agent = oldTraceAgent; + }); + + it('should leave out trace metadata if trace unavailable', () => { + loggingCommon.cloudLog.entry = (entryMetadata: {}, data: {}) => { + assert.deepStrictEqual(entryMetadata, { + resource: loggingCommon.resource, + }); + assert.deepStrictEqual(data, { + message: MESSAGE, + metadata: METADATA, + }); + }; + + const oldTraceAgent = global._google_trace_agent; + + global._google_trace_agent = {}; + loggingCommon.log(LEVEL, MESSAGE, METADATA, assert.ifError); + + global._google_trace_agent = { + getCurrentContextId: () => { + return null; + }, + getWriterProjectId: () => { + return null; + }, + }; + loggingCommon.log(LEVEL, MESSAGE, METADATA, assert.ifError); + + global._google_trace_agent = { + getCurrentContextId: () => { + return null; + }, + getWriterProjectId: () => { + return 'project1'; + }, + }; + loggingCommon.log(LEVEL, MESSAGE, METADATA, assert.ifError); + + global._google_trace_agent = { + getCurrentContextId: () => { + return 'trace1'; + }, + getWriterProjectId: () => { + return null; + }, + }; + loggingCommon.log(LEVEL, MESSAGE, METADATA, assert.ifError); + global._google_trace_agent = oldTraceAgent; + }); + + it('should write to the log', done => { + const entry = {}; + + loggingCommon.cloudLog.entry = () => { + return entry; + }; + + loggingCommon.cloudLog[STACKDRIVER_LEVEL] = ( + entry_: Entry[], + callback: () => void, + ) => { + assert.deepEqual(entry_[0], entry); + callback(); // done() + }; + + loggingCommon.log(LEVEL, MESSAGE, METADATA, done); + }); + + it('should add instrumentation log entry', done => { + loggingCommon.cloudLog.entry = (entryMetadata: {}, data: {}) => { + return new Entry(entryMetadata, data); + }; + loggingCommon.cloudLog['info'] = ( + entry_: Entry[], + callback: () => void, + ) => { + assert.equal(entry_.length, 2); + assert.equal( + entry_[1].data[instrumentation.DIAGNOSTIC_INFO_KEY][ + instrumentation.INSTRUMENTATION_SOURCE_KEY + ][0].name, + 'nodejs-winston', + ); + callback(); // done() + }; + instrumentation.setInstrumentationStatus(false); + loggingCommon.log(INFO, MESSAGE, METADATA, done); + }); + + it('should add instrumentation log entry with info log level', done => { + loggingCommon.cloudLog.entry = (entryMetadata: {}, data: {}) => { + return new Entry(entryMetadata, data); + }; + loggingCommon.cloudLog['info'] = (entry_: Entry[]) => { + assert.equal(entry_.length, 1); + assert.equal( + entry_[0].data[instrumentation.DIAGNOSTIC_INFO_KEY][ + instrumentation.INSTRUMENTATION_SOURCE_KEY + ][0].name, + 'nodejs-winston', + ); + }; + loggingCommon.cloudLog[STACKDRIVER_LEVEL] = (entry_: Entry[]) => { + assert.equal(entry_.length, 1); + assert.deepStrictEqual(entry_[0].data, { + message: MESSAGE, + metadata: METADATA, + }); + }; + instrumentation.setInstrumentationStatus(false); + loggingCommon.log(LEVEL, MESSAGE, METADATA); + done(); + }); + }); + + describe('label and labels', () => { + const LEVEL = Object.keys(OPTIONS.levels as {[name: string]: number})[0]; + const MESSAGE = 'message'; + const PREFIX = 'prefix'; + const LABELS = {label1: 'value1'}; + const METADATA: Metadata = {value: () => {}, labels: {label2: 'value2'}}; + + beforeEach(() => { + const opts = Object.assign({}, OPTIONS, { + prefix: PREFIX, + labels: LABELS, + }); + + loggingCommon = new loggingCommonLib.LoggingCommon(opts); + }); + + it('should properly create an entry with labels and [prefix] message', done => { + loggingCommon.cloudLog.entry = (entryMetadata1: {}, data1: {}) => { + assert.deepStrictEqual(entryMetadata1, { + resource: loggingCommon.resource, + // labels should have been merged. + labels: { + label1: 'value1', + label2: 'value2', + }, + }); + assert.deepStrictEqual(data1, { + message: `[${PREFIX}] ${MESSAGE}`, + metadata: METADATA, + }); + + const metadataWithoutLabels = Object.assign({}, METADATA); + delete metadataWithoutLabels.labels; + + loggingCommon.cloudLog.entry = (entryMetadata2: {}, data2: {}) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + console.log((entryMetadata2 as any).labels); + assert.deepStrictEqual(entryMetadata2, { + resource: loggingCommon.resource, + labels: {label1: 'value1'}, + }); + assert.deepStrictEqual(data2, { + message: `[${PREFIX}] ${MESSAGE}`, + metadata: METADATA, + }); + done(); + }; + + loggingCommon.log( + LEVEL, + MESSAGE, + metadataWithoutLabels, + assert.ifError, + ); + }; + + loggingCommon.log(LEVEL, MESSAGE, METADATA, assert.ifError); + }); + }); +}); diff --git a/handwritten/nodejs-logging-winston/test/index.ts b/handwritten/nodejs-logging-winston/test/index.ts new file mode 100644 index 00000000000..f8f30d1790e --- /dev/null +++ b/handwritten/nodejs-logging-winston/test/index.ts @@ -0,0 +1,203 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as assert from 'assert'; +import {describe, it, beforeEach} from 'mocha'; +import * as TransportStream from 'winston-transport'; +import * as proxyquire from 'proxyquire'; +import {Options} from '../src'; + +describe('logging-winston', () => { + let fakeLoggingOptions_: Options | null; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let lastFakeLoggingArgs: IArguments | any[] = []; + + class FakeLogging { + constructor(options: {}) { + fakeLoggingOptions_ = options; + } + log( + level: string, + message: string, + metadata: {} | undefined, + callback: () => void, + ): void { + // eslint-disable-next-line prefer-rest-params + lastFakeLoggingArgs = arguments; + if (callback) setImmediate(callback); + } + } + + class FakeTransport { + // transportCalledWith_ takes arguments which cannot be determined type. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + transportCalledWith_: any; + constructor() { + // eslint-disable-next-line prefer-rest-params + this.transportCalledWith_ = arguments; + } + } + + const fakeWinston = { + transports: {}, + Transport: FakeTransport, + }; + + const loggingWinstonLib = proxyquire('../src/index', { + './common': {LoggingCommon: FakeLogging}, + winston: fakeWinston, + }); + // loggingWinston is LoggingWinston namespace which cannot be determined type. + // eslint-disable-next-line + let loggingWinston: any; + + const OPTIONS: Options = { + logName: 'log-name', + levels: { + one: 1, + }, + resource: {}, + serviceContext: { + service: 'fake-service', + }, + apiEndpoint: 'fake.local', + }; + + beforeEach(() => { + fakeLoggingOptions_ = null; + //eslint-disable-next-line @typescript-eslint/no-unused-vars + loggingWinston = new loggingWinstonLib.LoggingWinston(OPTIONS); + }); + + describe('instantiation/options', () => { + it('should inherit from winston-transport.TransportStream', () => { + const loggingWinston = new loggingWinstonLib.LoggingWinston(OPTIONS); + assert.ok(loggingWinston instanceof TransportStream); + }); + + it('should initialize Log instance using provided scopes', () => { + const fakeScope = 'fake scope'; + + const optionsWithScopes: Options = Object.assign({}, OPTIONS); + optionsWithScopes.scopes = fakeScope; + // tslint:disable-next-line:no-unused-expression + new loggingWinstonLib.LoggingWinston(optionsWithScopes); + + assert.deepStrictEqual(fakeLoggingOptions_, optionsWithScopes); + }); + + it('should initialize Log instance using provided apiEndpoint', () => { + const options = Object.assign({}, OPTIONS); + new loggingWinstonLib.LoggingWinston(options); + assert.deepStrictEqual(fakeLoggingOptions_, options); + }); + + it('should pass the provided options.inspectMetadata', () => { + const optionsWithInspectMetadata = Object.assign({}, OPTIONS, { + inspectMetadata: true, + }); + + // tslint:disable-next-line:no-unused-expression + new loggingWinstonLib.LoggingWinston(optionsWithInspectMetadata); + assert.strictEqual(fakeLoggingOptions_!.inspectMetadata, true); + }); + + it('should pass provided levels', () => { + assert.strictEqual(fakeLoggingOptions_!.levels, OPTIONS.levels); + }); + + it('should pass Log instance using provided name', () => { + const logName = 'log-name-override'; + + const optionsWithLogName = Object.assign({}, OPTIONS); + optionsWithLogName.logName = logName; + // tslint:disable-next-line:no-unused-expression + new loggingWinstonLib.LoggingWinston(optionsWithLogName); + + assert.strictEqual(fakeLoggingOptions_!.logName, logName); + }); + + it('should pass the provided resource', () => { + assert.strictEqual(fakeLoggingOptions_!.resource, OPTIONS.resource); + }); + + it('should pass the provided service context', () => { + assert.strictEqual( + fakeLoggingOptions_!.serviceContext, + OPTIONS.serviceContext, + ); + }); + + it('should pass all parameters to TransportStream', () => { + const level = 'INFO'; + const format = 'FORMAT'; + const optionsWithTransportStreamparameters = Object.assign({}, OPTIONS, { + level: level, + format: format, + silent: true, + handleExceptions: true, + handleRejections: false, + }); + new loggingWinstonLib.LoggingWinston( + optionsWithTransportStreamparameters, + ); + assert.strictEqual(fakeLoggingOptions_!.level, level); + assert.strictEqual(fakeLoggingOptions_!.format, format); + assert.strictEqual(fakeLoggingOptions_!.silent, true); + assert.strictEqual(fakeLoggingOptions_!.handleExceptions, true); + assert.strictEqual(fakeLoggingOptions_!.handleRejections, false); + }); + }); + + describe('log', () => { + const LEVEL = Object.keys(OPTIONS.levels as {[name: string]: number})[0]; + const MESSAGE = 'message'; + const METADATA = {a: 1}; + + const loggingWinston = new loggingWinstonLib.LoggingWinston(); + + beforeEach(() => { + lastFakeLoggingArgs = []; + }); + + it('should properly call common.log', done => { + const args = Object.assign({}, METADATA, { + level: LEVEL, + message: MESSAGE, + }); + + loggingWinston.log(args); + + const [level, message, meta] = lastFakeLoggingArgs; + assert.strictEqual(level, 'one'); + assert.strictEqual(message, 'message'); + assert.deepStrictEqual(meta, {a: 1}); + done(); + }); + + it('should prefer Symbol for level', () => { + const info = { + ...METADATA, + message: MESSAGE, + level: `\u001b[34m${LEVEL}\u001b[39m`, + [Symbol.for('level')]: LEVEL, + }; + loggingWinston.log(info); + const [level, message, meta] = lastFakeLoggingArgs; + assert.strictEqual(level, 'one'); + assert.strictEqual(message, 'message'); + assert.deepStrictEqual(meta, {a: 1, [Symbol.for('level')]: LEVEL}); + }); + }); +}); diff --git a/handwritten/nodejs-logging-winston/test/middleware/express.ts b/handwritten/nodejs-logging-winston/test/middleware/express.ts new file mode 100644 index 00000000000..96ffd7750fe --- /dev/null +++ b/handwritten/nodejs-logging-winston/test/middleware/express.ts @@ -0,0 +1,163 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as assert from 'assert'; +import {describe, it, beforeEach} from 'mocha'; +import {GCPEnv} from 'google-auth-library'; +import {LogEntry} from 'winston'; +import * as TransportStream from 'winston-transport'; +import * as winston from 'winston'; +import * as proxyquire from 'proxyquire'; +import {Options} from '../../src'; + +// types-only import. Actual require is done through proxyquire below. + +const FAKE_PROJECT_ID = 'project-🦄'; +const FAKE_GENERATED_MIDDLEWARE = () => {}; +const FAKE_ENVIRONMENT = 'FAKE_ENVIRONMENT'; + +let authEnvironment: string; +let passedOptions: Array; +let transport: TransportStream | undefined; + +class FakeLoggingWinston extends TransportStream { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + common: any; + + constructor(options: Options) { + super(options); + // eslint-disable-next-line @typescript-eslint/no-this-alias + transport = this; + passedOptions.push(options); + this.common = { + cloudLog: { + logging: { + auth: { + async getProjectId() { + return FAKE_PROJECT_ID; + }, + async getEnv() { + return authEnvironment; + }, + }, + }, + }, + }; + } + + log(info: LogEntry, cb: Function) { + cb(); + } +} + +let passedProjectId: string | undefined; +let passedEmitRequestLog: Function | undefined; +function fakeMakeMiddleware( + projectId: string, + makeChildLogger: Function, + emitRequestLog: Function, +): Function { + passedProjectId = projectId; + passedEmitRequestLog = emitRequestLog; + return FAKE_GENERATED_MIDDLEWARE; +} + +const {makeMiddleware} = proxyquire('../../src/middleware/express', { + '../index': {LoggingWinston: FakeLoggingWinston}, + '@google-cloud/logging': { + middleware: {express: {makeMiddleware: fakeMakeMiddleware}}, + }, +}); + +describe('middleware/express', () => { + let logger: winston.Logger; + + beforeEach(() => { + logger = winston.createLogger(); + transport = undefined; + passedOptions = []; + passedProjectId = undefined; + passedEmitRequestLog = undefined; + authEnvironment = FAKE_ENVIRONMENT; + }); + + it('should create and return a middleware', async () => { + const mw = await makeMiddleware(logger); + assert.strictEqual(mw, FAKE_GENERATED_MIDDLEWARE); + }); + + it('should not allocate a transport when passed', async () => { + const t = new FakeLoggingWinston({}); + assert.strictEqual(transport, t); + await makeMiddleware(logger, t); + assert.strictEqual( + transport, + t, + 'makeMiddleware should not construct a transport', + ); + }); + + it('should not allocate a transport when it can be inferred', async () => { + const t = new FakeLoggingWinston({}); + logger = winston.createLogger({ + transports: [t], + }); + await makeMiddleware(logger); + assert.strictEqual(logger.transports.length, 1); + assert.strictEqual(logger.transports[0], t); + }); + + it('should add a transport to the logger when not provided', async () => { + await makeMiddleware(logger); + assert.strictEqual(logger.transports.length, 1); + assert.strictEqual(logger.transports[0], transport); + }); + + it('should add a user provided transport to the logger', async () => { + const t = new FakeLoggingWinston({}); + await makeMiddleware(logger, t); + assert.strictEqual(logger.transports.length, 1); + assert.strictEqual(logger.transports[0], t); + }); + + it('should create a transport with the correct logName', async () => { + await makeMiddleware(logger); + assert.ok(passedOptions); + assert.strictEqual(passedOptions.length, 1); + const [options] = passedOptions; + assert.strictEqual(options!.logName, 'winston_log'); + }); + + it('should acquire the projectId and pass to makeMiddleware', async () => { + await makeMiddleware(logger); + assert.strictEqual(passedProjectId, FAKE_PROJECT_ID); + }); + + [GCPEnv.APP_ENGINE, GCPEnv.CLOUD_FUNCTIONS, GCPEnv.CLOUD_RUN].forEach(env => { + it(`should not generate the request logger on ${env}`, async () => { + authEnvironment = env; + const t = new FakeLoggingWinston({}); + if (env === GCPEnv.CLOUD_RUN) { + // Cloud Run needs explicit set skipParentEntryForCloudRun flag to enable this behavior until we can make breaking change in next major version + await makeMiddleware(logger, t, /*skipParentEntryForCloudRun=*/ true); + } else { + await makeMiddleware(logger, t); + } + assert.ok(passedOptions); + assert.strictEqual(passedOptions.length, 1); + // emitRequestLog parameter to makeChildLogger should be undefined. + assert.strictEqual(passedEmitRequestLog, undefined); + }); + }); +}); diff --git a/handwritten/nodejs-logging-winston/test/middleware/make-child-logger.ts b/handwritten/nodejs-logging-winston/test/middleware/make-child-logger.ts new file mode 100644 index 00000000000..66c4c94b0b2 --- /dev/null +++ b/handwritten/nodejs-logging-winston/test/middleware/make-child-logger.ts @@ -0,0 +1,129 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as assert from 'assert'; +import {describe, it, afterEach} from 'mocha'; +import * as winston from 'winston'; +import { + LOGGING_TRACE_KEY, + LOGGING_SPAN_KEY, + LOGGING_SAMPLED_KEY, +} from '../../src/common'; + +import {makeChildLogger} from '../../src/middleware/make-child-logger'; + +describe('makeChildLogger', () => { + const FAKE_TRACE = '🤥'; + const FAKE_SPAN = '☂️'; + const FAKE_SAMPLE = true; + const LOGGER = winston.createLogger({ + transports: [new winston.transports.Console({silent: true})], + }); + const origWrite = LOGGER.write; + + afterEach(() => { + LOGGER.write = origWrite; + }); + + it('should return a winston-like logger', () => { + const child = makeChildLogger(LOGGER, FAKE_TRACE); + let logEntry: winston.LogEntry; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (LOGGER.write as any) = (logEntry_: winston.LogEntry) => { + logEntry = logEntry_; + }; + + child.info('hello'); + assert.strictEqual(logEntry!.message, 'hello'); + assert.strictEqual(logEntry!.level, 'info'); + + child.error('👾', {key: '🎃'}); + assert.strictEqual(logEntry!.message, '👾'); + assert.strictEqual(logEntry!.level, 'error'); + assert.strictEqual(logEntry!.key, '🎃'); + + child.warn('hello %d', 56, {key: 'value'}); + assert.strictEqual(logEntry!.message, 'hello %d'); + assert.strictEqual(logEntry!.level, 'warn'); + assert.strictEqual(logEntry!.key, undefined); + + child.log('silly', '🎈'); + assert.strictEqual(logEntry!.message, '🎈'); + assert.strictEqual(logEntry!.level, 'silly'); + }); + + it('should override only the write function', () => { + const child = makeChildLogger(LOGGER, FAKE_TRACE); + assert.strictEqual(child.warn, LOGGER.warn); + assert.notStrictEqual(child.write, LOGGER.write); + }); + + it('should inject LOGGING_TRACE_KEY only into the metadata', () => { + const child = makeChildLogger(LOGGER, FAKE_TRACE); + let trace; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (LOGGER.write as any) = (info: winston.LogEntry) => { + trace = info[LOGGING_TRACE_KEY]; + }; + child.debug('hello world'); + assert.strictEqual(trace, FAKE_TRACE); + }); + + it('should inject the LOGGING_SPAN_KEY into the metadata', () => { + const child = makeChildLogger(LOGGER, FAKE_TRACE, FAKE_SPAN); + let trace, span; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (LOGGER.write as any) = (info: winston.LogEntry) => { + trace = info[LOGGING_TRACE_KEY]; + span = info[LOGGING_SPAN_KEY]; + }; + child.debug('hello world'); + assert.strictEqual(trace, FAKE_TRACE); + assert.strictEqual(span, FAKE_SPAN); + }); + + it('should inject the LOGGING_SAMPLED_KEY into the metadata', () => { + const child = makeChildLogger(LOGGER, FAKE_TRACE, FAKE_SPAN, FAKE_SAMPLE); + let trace, span, sample; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (LOGGER.write as any) = (info: winston.LogEntry) => { + trace = info[LOGGING_TRACE_KEY]; + span = info[LOGGING_SPAN_KEY]; + sample = info[LOGGING_SAMPLED_KEY]; + }; + child.debug('hello world'); + assert.strictEqual(trace, FAKE_TRACE); + assert.strictEqual(span, FAKE_SPAN); + assert.strictEqual(sample, FAKE_SAMPLE); + }); + + it('should not overwrite existing LOGGING_X_KEY values', () => { + const child = makeChildLogger(LOGGER, FAKE_TRACE, FAKE_SPAN, FAKE_SAMPLE); + let trace, span, sample; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (LOGGER.write as any) = (info: winston.LogEntry) => { + trace = info[LOGGING_TRACE_KEY]; + span = info[LOGGING_SPAN_KEY]; + sample = info[LOGGING_SAMPLED_KEY]; + }; + child.debug('hello world', { + [LOGGING_TRACE_KEY]: 'to-be-clobbered', + [LOGGING_SPAN_KEY]: 'to-be-clobbered', + [LOGGING_SAMPLED_KEY]: false, + }); + assert.notStrictEqual(trace, FAKE_TRACE); + assert.notStrictEqual(span, FAKE_SPAN); + assert.notStrictEqual(sample, FAKE_SAMPLE); + }); +}); diff --git a/handwritten/nodejs-logging-winston/test/stackdriver-trace-integration.ts b/handwritten/nodejs-logging-winston/test/stackdriver-trace-integration.ts new file mode 100644 index 00000000000..3452096f8be --- /dev/null +++ b/handwritten/nodejs-logging-winston/test/stackdriver-trace-integration.ts @@ -0,0 +1,198 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as assert from 'assert'; +import {describe, it, before, beforeEach, after} from 'mocha'; +import * as proxyquire from 'proxyquire'; +import * as winston from 'winston'; +import * as loggingWinstonLibTypes from '../src/index'; + +declare const global: {[index: string]: {} | null}; + +/** + * Tests that ensure that getDefaultMetadataForTracing can be used for + * trace-log correlation when Cloud Trace Agent is present. See + * src/default-metadata.ts for an explanation on why this to exist. + */ +describe('Cloud Trace Log Correlation', () => { + // Trace context IDs seen in logs so far in a test. + const seenContextIds: string[] = []; + // Set a trace context ID for all succeeding Winston logs. + let setCurrentContextId: (id: string) => void; + // The Cloud Logging Winston transport library. + let loggingWinstonLib: typeof loggingWinstonLibTypes; + // The flag indicating if callback was called or not + let isCallbackCalled: boolean; + + class FakeLogging { + constructor() {} + log(data: never, callback: () => void) { + if (typeof callback === 'function') setImmediate(callback); + return this; + } + // Stub entry to record the incoming trace context ID. + entry(metadata: {trace: string}) { + if (metadata.trace) { + const traceId = metadata.trace.split('/')[3]; + assert.ok(traceId); + seenContextIds.push(traceId); + } else { + seenContextIds.push(''); + } + return {}; + } + info(data: never, callback: () => void) { + return this.log(data, callback); + } + } + + before(() => { + loggingWinstonLib = proxyquire('../src/index', { + '@google-cloud/logging': {'@global': true, Logging: FakeLogging}, + }); + }); + + beforeEach(() => { + seenContextIds.length = 0; + isCallbackCalled = false; + setCurrentContextId = (() => { + let currentContextId: string; + global._google_trace_agent = { + getCurrentContextId: () => { + return currentContextId; + }, + getWriterProjectId: () => { + return 'project1'; + }, + }; + return (id: string) => { + currentContextId = id; + }; + })(); + }); + + after(() => { + delete global._google_trace_agent; + }); + + it('Works when using supporting default metadata', done => { + const transport = new loggingWinstonLib.LoggingWinston(); + const logger = winston.createLogger({ + transports: [transport], + defaultMeta: loggingWinstonLib.getDefaultMetadataForTracing(), + }); + setCurrentContextId('1'); + logger.log({level: 'info', message: 'hello'}); + setCurrentContextId('2'); + logger.log({level: 'info', message: 'hello'}); + setCurrentContextId('3'); + setImmediate(() => { + assert.strictEqual(seenContextIds.length, 3); + assert.deepStrictEqual(seenContextIds, ['1', '', '2']); + done(); + }); + }); + + /** + * This test is just like the previous one, but without using + * getDefaultMetadataForTracing. The expected seen context IDs are ['1', '3'], + * which are wrong. + * If this test ever fails, that signals that getDefaultMetadataForTracing + * may no longer be a necessary API, as Winston 3 has fixed its context + * propagation issue. + */ + it('Does not work without using supporting default metadata', done => { + const transport = new loggingWinstonLib.LoggingWinston(); + const logger = winston.createLogger({ + transports: [transport], + }); + setCurrentContextId('1'); + logger.log({level: 'info', message: 'hello'}); + setCurrentContextId('2'); + logger.log({level: 'info', message: 'hello'}); + setCurrentContextId('3'); + setImmediate(() => { + assert.strictEqual(seenContextIds.length, 2); + assert.throws(() => { + assert.deepStrictEqual(seenContextIds, ['1', '2']); + }); + done(); + }); + }); + + it('Calls default callback when present', done => { + const transport = new loggingWinstonLib.LoggingWinston({ + defaultCallback: () => { + isCallbackCalled = true; + }, + }); + const logger = winston.createLogger({ + transports: [transport], + }); + logger.log({level: 'info', message: 'hello'}); + setImmediate(() => { + assert.strictEqual(isCallbackCalled, true); + done(); + }); + }); + + [null, {}, {getWriterProjectId: () => 'project1'}].forEach(testCase => { + it(`Doesn't crash when a non-compatible Trace Agent is present: ${testCase}`, done => { + global._google_trace_agent = testCase; + const transport = new loggingWinstonLib.LoggingWinston(); + const logger = winston.createLogger({ + transports: [transport], + defaultMeta: loggingWinstonLib.getDefaultMetadataForTracing(), + }); + setCurrentContextId('1'); + logger.log({level: 'info', message: 'hello'}); + setCurrentContextId('2'); + logger.log({level: 'info', message: 'hello'}); + setCurrentContextId('3'); + setImmediate(() => { + assert.strictEqual(seenContextIds.length, 2); + done(); + }); + }); + }); + + [ + { + getCurrentContextId: () => 'trace1', + getWriterProjectId: () => null, + }, + { + getCurrentContextId: () => null, + getWriterProjectId: () => 'project1', + }, + ].forEach(testCase => { + it(`Doesn't crash when a Trace Agent field is not present: ${testCase}`, done => { + global._google_trace_agent = testCase; + const transport = new loggingWinstonLib.LoggingWinston(); + const logger = winston.createLogger({ + transports: [transport], + defaultMeta: loggingWinstonLib.getDefaultMetadataForTracing(), + }); + setCurrentContextId('1'); + logger.log({level: 'info', message: 'hello'}); + setCurrentContextId('2'); + logger.log({level: 'info', message: 'hello'}); + setCurrentContextId('3'); + setImmediate(() => { + assert.strictEqual(seenContextIds.length, 2); + done(); + }); + }); + }); +}); diff --git a/handwritten/nodejs-logging-winston/tsconfig.json b/handwritten/nodejs-logging-winston/tsconfig.json new file mode 100644 index 00000000000..3246c2cfb55 --- /dev/null +++ b/handwritten/nodejs-logging-winston/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "./node_modules/gts/tsconfig-google.json", + "compilerOptions": { + "rootDir": ".", + "outDir": "build", + "lib": [ + "es2018", + "dom" + ] + }, + "include": [ + "src/*.ts", + "src/**/*.ts", + "test/*.ts", + "test/**/*.ts", + "system-test/*.ts", + "system-test/**/*.ts" + ] +} diff --git a/release-please-config.json b/release-please-config.json index 9c211610789..41bc5b99050 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -1,6 +1,8 @@ { + "bump-minor-pre-major": true, "initial-version": "0.1.0", "packages": { + "handwritten/nodejs-logging-winston": {}, "packages/gapic-node-processing": {}, "packages/google-ads-admanager": {}, "packages/google-ads-datamanager": {}, @@ -149,11 +151,11 @@ "packages/google-cloud-saasplatform-saasservicemgmt": {}, "packages/google-cloud-scheduler": {}, "packages/google-cloud-secretmanager": {}, + "packages/google-cloud-securesourcemanager": {}, "packages/google-cloud-security-privateca": {}, "packages/google-cloud-security-publicca": {}, "packages/google-cloud-securitycenter": {}, "packages/google-cloud-securitycentermanagement": {}, - "packages/google-cloud-securesourcemanager": {}, "packages/google-cloud-servicedirectory": {}, "packages/google-cloud-servicehealth": {}, "packages/google-cloud-shell": {}, @@ -227,6 +229,5 @@ "type": "sentence-case" } ], - "bump-minor-pre-major": true, "release-type": "node" } \ No newline at end of file