diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 9831883b51c..5a0779bc5fe 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,4 +1,5 @@
{
+ "handwritten/nodejs-logging-bunyan": "5.1.0",
"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",
@@ -217,4 +218,4 @@
"packages/google-streetview-publish": "0.4.1",
"packages/grafeas": "6.1.1",
"packages/typeless-sample-bot": "3.1.1"
-}
\ No newline at end of file
+}
diff --git a/handwritten/nodejs-logging-bunyan/.OwlBot.yaml b/handwritten/nodejs-logging-bunyan/.OwlBot.yaml
new file mode 100644
index 00000000000..10389756341
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/.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-bunyan/.eslintignore b/handwritten/nodejs-logging-bunyan/.eslintignore
new file mode 100644
index 00000000000..c4a0963e9bd
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/.eslintignore
@@ -0,0 +1,8 @@
+**/node_modules
+**/coverage
+test/fixtures
+build/
+docs/
+protos/
+samples/generated/
+system-test/**/fixtures
diff --git a/handwritten/nodejs-logging-bunyan/.eslintrc.json b/handwritten/nodejs-logging-bunyan/.eslintrc.json
new file mode 100644
index 00000000000..78215349546
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "./node_modules/gts"
+}
diff --git a/handwritten/nodejs-logging-bunyan/.gitattributes b/handwritten/nodejs-logging-bunyan/.gitattributes
new file mode 100644
index 00000000000..33739cb74e4
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/.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-bunyan/.gitignore b/handwritten/nodejs-logging-bunyan/.gitignore
new file mode 100644
index 00000000000..5b1296e1703
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/.gitignore
@@ -0,0 +1,16 @@
+**/*.log
+**/node_modules
+.coverage
+.nyc_output
+docs/
+out/
+build/
+system-test/secrets.js
+system-test/*key.json
+*.lock
+.DS_Store
+google-cloud-logging-winston-*.tgz
+google-cloud-logging-bunyan-*.tgz
+package-lock.json
+key.json
+__pycache__
diff --git a/handwritten/nodejs-logging-bunyan/.jsdoc.js b/handwritten/nodejs-logging-bunyan/.jsdoc.js
new file mode 100644
index 00000000000..bf83cc4b172
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/.jsdoc.js
@@ -0,0 +1,51 @@
+// Copyright 2019 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 2019 Google, LLC.',
+ includeDate: false,
+ sourceFiles: false,
+ systemName: '@google-cloud/logging-bunyan',
+ theme: 'lumen',
+ default: {
+ "outputSourceFiles": false
+ }
+ },
+ markdown: {
+ idInHeadings: true
+ }
+};
diff --git a/handwritten/nodejs-logging-bunyan/.mocharc.js b/handwritten/nodejs-logging-bunyan/.mocharc.js
new file mode 100644
index 00000000000..0b600509bed
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/.mocharc.js
@@ -0,0 +1,29 @@
+// Copyright 2020 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-bunyan/.nycrc b/handwritten/nodejs-logging-bunyan/.nycrc
new file mode 100644
index 00000000000..b18d5472b62
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/.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-bunyan/.prettierignore b/handwritten/nodejs-logging-bunyan/.prettierignore
new file mode 100644
index 00000000000..9340ad9b86d
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/.prettierignore
@@ -0,0 +1,6 @@
+**/node_modules
+**/coverage
+test/fixtures
+build/
+docs/
+protos/
diff --git a/handwritten/nodejs-logging-bunyan/.prettierrc.js b/handwritten/nodejs-logging-bunyan/.prettierrc.js
new file mode 100644
index 00000000000..d1b95106f4c
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/.prettierrc.js
@@ -0,0 +1,17 @@
+// Copyright 2020 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-bunyan/.readme-partials.yml b/handwritten/nodejs-logging-bunyan/.readme-partials.yml
new file mode 100644
index 00000000000..38bc9f9b622
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/.readme-partials.yml
@@ -0,0 +1,186 @@
+introduction: |-
+ This module provides an easy to use, higher-level layer for working with [Cloud Logging](https://cloud.google.com/logging/docs),
+ compatible with [Bunyan](https://www.npmjs.com/package/bunyan). Simply attach this as a transport to your existing Bunyan loggers.
+body: |-
+ ### 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:
+
+ 
+
+ The middleware adds a `bunyan`-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 lb = require('@google-cloud/logging-bunyan');
+
+ // Import express module and create an http server.
+ const express = require('express');
+
+ async function startServer() {
+ const {logger, mw} = await lb.express.middleware();
+ const app = express();
+
+ // Install the logging middleware. This ensures that a Bunyan-style `log`
+ // function is available on the `request` object. Attach this as one of the
+ // earliest middleware to make sure that log function is available in all the
+ // 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 bunyan 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 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({port: 8080}, 'bonjour');
+
+ // Start listening on the http server.
+ app.listen(8080, () => {
+ console.log('http server listening on port 8080');
+ });
+ }
+
+ startServer();
+ ```
+
+ ### Error Reporting
+
+ Any `Error` objects you log at severity `error` or higher can automatically be picked up by [Cloud Error Reporting](https://cloud.google.com/error-reporting/) if you have specified a `serviceContext.service` when instantiating a `LoggingBunyan` instance:
+
+ ```javascript
+ const loggingBunyan = new LoggingBunyan({
+ serviceContext: {
+ service: 'my-service', // required to report logged errors
+ // to the Google Cloud 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][@google-cloud/error-reporting] module which provides direct access to the Error Reporting API.
+
+ ### Special Payload Fields in LogEntry
+
+ There are some fields that are considered special by Google cloud logging and will be extracted into the LogEntry structure. For example, `severity`, `message` and `labels` can be extracted to LogEntry if included in the bunyan log payload. These [special JSON fields](https://cloud.google.com/logging/docs/structured-logging#special-payload-fields) will be used to set the corresponding fields in the `LogEntry`. Please be aware of these special fields to avoid unexpected logging behavior.
+
+ ### LogEntry Labels
+
+ If the bunyan log record contains a label property where all the values are strings, we automatically promote that
+ property to be the [`LogEntry.labels`](https://cloud.google.com/logging/docs/reference/v2/rpc/google.logging.v2#logentry) value rather
+ than being one of the properties in the `payload` fields. This makes it easier to filter the logs in the UI using the labels.
+
+ ```javascript
+ logger.info({labels: {someKey: 'some value'}}, 'test log message');
+ ```
+
+ All the label values must be strings for this automatic promotion to work. Otherwise the labels are left in the payload.
+
+ ### Formatting Request Logs
+
+ To format your request logs you can provide a `httpRequest` property on the bunyan metadata you provide along with the log message. 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:
+
+ 
+
+ ```js
+ logger.info({
+ httpRequest: {
+ status: res.statusCode,
+ requestUrl: req.url,
+ requestMethod: req.method,
+ remoteIp: req.connection.remoteAddress,
+ // etc.
+ }
+ }, req.path);
+ ```
+
+ 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: the linked protobuf documentation shows `snake_case` property names, but in JavaScript one needs to provide property names in `camelCase`.)
+
+ ### Correlating Logs with Traces
+
+ If you use [@google-cloud/trace-agent][trace-agent] module, then this module will set the Cloud Logging [LogEntry][LogEntry] `trace` property based on the current trace context when available. That correlation allows you to [view log entries][trace-viewing-log-entries] inline with trace spans in the Cloud Trace Viewer. Example:
+
+ 
+
+ If you wish to set the Cloud LogEntry `trace` property with a custom value, then write a Bunyan log entry property for `'logging.googleapis.com/trace'`, which is exported by this module as `LOGGING_TRACE_KEY`. For example:
+
+ ```js
+ const bunyan = require('bunyan');
+ // Node 6+
+ const {LoggingBunyan, LOGGING_TRACE_KEY} = require('@google-cloud/logging-bunyan');
+ const loggingBunyan = LoggingBunyan();
+
+ ...
+
+ logger.info({
+ [LOGGING_TRACE_KEY]: 'custom-trace-value'
+ }, 'Bunyan log entry with custom trace field');
+ ```
+
+ ### Error handling with a default callback
+ The `LoggingBunyan` class creates an instance of `Logging` which creates 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 when it fails or an error is returned from Logging backend.
+ If the error is not handled, it could crash the application. One possible way to handle the error is to provide a default callback
+ to the `LoggingBunyan` constructor which will be used to initialize the `Log` object with that callback like in the example below:
+
+ ```js
+ // Imports the Google Cloud client library for Bunyan
+ const {LoggingBunyan} = require('@google-cloud/logging-bunyan');
+ // Creates a client
+ const loggingBunyan = new LoggingBunyan({
+ projectId: 'your-project-id',
+ keyFilename: '/path/to/key.json',
+ defaultCallback: err => {
+ if (err) {
+ console.log('Error occured: ' + err);
+ }
+ },
+ });
+ ```
+
+ ### 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 `LoggingBunyan` 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 Bunyan
+ const {LoggingBunyan} = require('@google-cloud/logging-bunyan');
+
+ // Creates a client
+ const loggingBunyan = new LoggingBunyan({
+ projectId: 'your-project-id',
+ keyFilename: '/path/to/key.json',
+ redirectToStdout: true,
+ });
+ ```
diff --git a/handwritten/nodejs-logging-bunyan/.repo-metadata.json b/handwritten/nodejs-logging-bunyan/.repo-metadata.json
new file mode 100644
index 00000000000..fe857deb569
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/.repo-metadata.json
@@ -0,0 +1,14 @@
+{
+ "name": "logging-bunyan",
+ "name_pretty": "Cloud Logging for Bunyan",
+ "product_documentation": "https://cloud.google.com/logging",
+ "client_documentation": "https://cloud.google.com/nodejs/docs/reference/logging-bunyan/latest",
+ "issue_tracker": "https://issuetracker.google.com/savedsearches/559764",
+ "release_level": "stable",
+ "language": "nodejs",
+ "repo": "googleapis/google-cloud-node",
+ "distribution_name": "@google-cloud/logging-bunyan",
+ "api_id": "logging.googleapis.com",
+ "codeowner_team": "@googleapis/yoshi-nodejs",
+ "library_type": "OTHER"
+}
diff --git a/handwritten/nodejs-logging-bunyan/CHANGELOG.md b/handwritten/nodejs-logging-bunyan/CHANGELOG.md
new file mode 100644
index 00000000000..39fdfd562ac
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/CHANGELOG.md
@@ -0,0 +1,479 @@
+# Changelog
+
+[npm history][1]
+
+[1]: https://www.npmjs.com/package/nodejs-logging-bunyan?activeTab=versions
+
+## [5.1.0](https://github.com/googleapis/nodejs-logging-bunyan/compare/v5.0.1...v5.1.0) (2024-01-31)
+
+
+### Features
+
+* Enable custom json fields truncation for bunyan ([#732](https://github.com/googleapis/nodejs-logging-bunyan/issues/732)) ([eb6fa66](https://github.com/googleapis/nodejs-logging-bunyan/commit/eb6fa6615cd43f192c07f69a5329a5d4ba159206))
+
+
+### Bug Fixes
+
+* Add speciel logging fields section in the doc ([#728](https://github.com/googleapis/nodejs-logging-bunyan/issues/728)) ([0b9ad2b](https://github.com/googleapis/nodejs-logging-bunyan/commit/0b9ad2b039ea282caf42284512aa61aaf2ed89e1))
+* Fix rest links with the correct gRPC links used by library. ([#724](https://github.com/googleapis/nodejs-logging-bunyan/issues/724)) ([555c33e](https://github.com/googleapis/nodejs-logging-bunyan/commit/555c33e858248b911a5aa405d11f64a2fb5942dd))
+
+## [5.0.1](https://github.com/googleapis/nodejs-logging-bunyan/compare/v5.0.0...v5.0.1) (2023-10-30)
+
+
+### Bug Fixes
+
+* **deps:** Update dependency @google-cloud/logging to v11 ([d740098](https://github.com/googleapis/nodejs-logging-bunyan/commit/d74009850b316d57a686637f3b0242fa72339000))
+
+## [5.0.0](https://github.com/googleapis/nodejs-logging-bunyan/compare/v4.2.2...v5.0.0) (2023-08-10)
+
+
+### ⚠ BREAKING CHANGES
+
+* upgrade to Node 14 ([#705](https://github.com/googleapis/nodejs-logging-bunyan/issues/705))
+
+### Miscellaneous Chores
+
+* Upgrade to Node 14 ([#705](https://github.com/googleapis/nodejs-logging-bunyan/issues/705)) ([e2299d6](https://github.com/googleapis/nodejs-logging-bunyan/commit/e2299d66ec80a8d5b5c9f15c36b071771e28f9b9))
+
+## [4.2.2](https://github.com/googleapis/nodejs-logging-bunyan/compare/v4.2.1...v4.2.2) (2022-12-02)
+
+
+### Bug Fixes
+
+* Add a partner team as approvers for PRs ([#677](https://github.com/googleapis/nodejs-logging-bunyan/issues/677)) ([7b88e97](https://github.com/googleapis/nodejs-logging-bunyan/commit/7b88e97aab7b70e0ccdf3c388edde013ac64707f))
+
+## [4.2.1](https://github.com/googleapis/nodejs-logging-bunyan/compare/v4.2.0...v4.2.1) (2022-11-07)
+
+
+### Bug Fixes
+
+* Switch instrumentation code to return version stored in NODEJS_BUNYAN_DEFAULT_LIBRARY_VERSION ([#672](https://github.com/googleapis/nodejs-logging-bunyan/issues/672)) ([2eb88d8](https://github.com/googleapis/nodejs-logging-bunyan/commit/2eb88d878583a2a8f823fa310353eeb6be931f7a))
+
+## [4.2.0](https://github.com/googleapis/nodejs-logging-bunyan/compare/v4.1.5...v4.2.0) (2022-11-04)
+
+
+### Features
+
+* Add support for instrumentation version annotations ([#670](https://github.com/googleapis/nodejs-logging-bunyan/issues/670)) ([e332a76](https://github.com/googleapis/nodejs-logging-bunyan/commit/e332a7626dd637daab4e23b36ca7249684506501))
+
+## [4.1.5](https://github.com/googleapis/nodejs-logging-bunyan/compare/v4.1.4...v4.1.5) (2022-11-01)
+
+
+### Bug Fixes
+
+* Prevent instrumentation crash and fix the system test ([#666](https://github.com/googleapis/nodejs-logging-bunyan/issues/666)) ([4e12496](https://github.com/googleapis/nodejs-logging-bunyan/commit/4e12496589123995ebf8e1c54a613c1d2ed565c5))
+
+## [4.1.4](https://github.com/googleapis/nodejs-logging-bunyan/compare/v4.1.3...v4.1.4) (2022-10-12)
+
+
+### Bug Fixes
+
+* Instrumentation performance ([#661](https://github.com/googleapis/nodejs-logging-bunyan/issues/661)) ([c0338fb](https://github.com/googleapis/nodejs-logging-bunyan/commit/c0338fba10e1a60373fdc74739f9237d0266413b))
+
+## [4.1.3](https://github.com/googleapis/nodejs-logging-bunyan/compare/v4.1.2...v4.1.3) (2022-10-10)
+
+
+### Bug Fixes
+
+* Skip parent request entry on cloud run ([#658](https://github.com/googleapis/nodejs-logging-bunyan/issues/658)) ([226972e](https://github.com/googleapis/nodejs-logging-bunyan/commit/226972e12aec94936f4d0b00277cbff05971726e))
+
+## [4.1.2](https://github.com/googleapis/nodejs-logging-bunyan/compare/v4.1.1...v4.1.2) (2022-08-25)
+
+
+### Bug Fixes
+
+* remove pip install statements ([#1546](https://github.com/googleapis/nodejs-logging-bunyan/issues/1546)) ([#649](https://github.com/googleapis/nodejs-logging-bunyan/issues/649)) ([0fdd9ec](https://github.com/googleapis/nodejs-logging-bunyan/commit/0fdd9ec12ad76518e630d0f51f69e34a78b6e693))
+* Update latest logging-nodejs to repaire google-gax vulnerability ([#651](https://github.com/googleapis/nodejs-logging-bunyan/issues/651)) ([857de69](https://github.com/googleapis/nodejs-logging-bunyan/commit/857de693a20e142a16468a6cb352295acd1249a5))
+
+## [4.1.1](https://github.com/googleapis/nodejs-logging-bunyan/compare/v4.1.0...v4.1.1) (2022-07-18)
+
+
+### Bug Fixes
+
+* Logging to stdout in Cloud Run creates a JSON object as "message" ([#644](https://github.com/googleapis/nodejs-logging-bunyan/issues/644)) ([41eaaa8](https://github.com/googleapis/nodejs-logging-bunyan/commit/41eaaa8e2dd282e625412a10907c2dd0f13cf1cb))
+
+## [4.1.0](https://github.com/googleapis/nodejs-logging-bunyan/compare/v4.0.0...v4.1.0) (2022-06-02)
+
+
+### Features
+
+* Add support for library instrumentation ([#631](https://github.com/googleapis/nodejs-logging-bunyan/issues/631)) ([39e0193](https://github.com/googleapis/nodejs-logging-bunyan/commit/39e0193bba4adb5945ff21b53002d2402c9505c5))
+
+## [4.0.0](https://github.com/googleapis/nodejs-logging-bunyan/compare/v3.3.1...v4.0.0) (2022-05-20)
+
+
+### ⚠ BREAKING CHANGES
+
+* update library to use Node 12 (#627)
+
+### Build System
+
+* update library to use Node 12 ([#627](https://github.com/googleapis/nodejs-logging-bunyan/issues/627)) ([e9d1143](https://github.com/googleapis/nodejs-logging-bunyan/commit/e9d1143ee335b825a3817fc09573da7cac4584c2))
+
+### [3.3.1](https://github.com/googleapis/nodejs-logging-bunyan/compare/v3.3.0...v3.3.1) (2022-04-15)
+
+
+### Bug Fixes
+
+* Reenable staleness bot ([#613](https://github.com/googleapis/nodejs-logging-bunyan/issues/613)) ([1b3f273](https://github.com/googleapis/nodejs-logging-bunyan/commit/1b3f273ea84475ae2d6fabed435775bfea253ae9))
+
+## [3.3.0](https://github.com/googleapis/nodejs-logging-bunyan/compare/v3.2.2...v3.3.0) (2022-03-21)
+
+
+### Features
+
+* Logging provider for Cloud Functions that outputs structured logs to process.stdout ([#605](https://github.com/googleapis/nodejs-logging-bunyan/issues/605)) ([f3ed3aa](https://github.com/googleapis/nodejs-logging-bunyan/commit/f3ed3aa973d0e2cd1f7311ef48a69cbd72df80f1))
+
+### [3.2.2](https://github.com/googleapis/nodejs-logging-bunyan/compare/v3.2.1...v3.2.2) (2022-03-09)
+
+
+### Bug Fixes
+
+* Use defaultCallback in LoggingBunyan class ([#601](https://github.com/googleapis/nodejs-logging-bunyan/issues/601)) ([f4c01ab](https://github.com/googleapis/nodejs-logging-bunyan/commit/f4c01abe9ee46d89494caa03618500f3a11ee78a))
+
+### [3.2.1](https://github.com/googleapis/nodejs-logging-bunyan/compare/v3.2.0...v3.2.1) (2022-03-02)
+
+
+### Bug Fixes
+
+* Update dependency @google-cloud/logging from 9.0.0 to 9.8.0 ([#597](https://github.com/googleapis/nodejs-logging-bunyan/issues/597)) ([a350362](https://github.com/googleapis/nodejs-logging-bunyan/commit/a350362ba78f275e4c294bfdf2ea5d868191e87e))
+
+## [3.2.0](https://www.github.com/googleapis/nodejs-logging-bunyan/compare/v3.1.1...v3.2.0) (2021-12-09)
+
+
+### Features
+
+* add eslintignore for sameple generated code ([#1302](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/1302)) ([#574](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/574)) ([10c6371](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/10c63713d659f36f45eeb23adba554ed159623a5))
+
+### [3.1.1](https://www.github.com/googleapis/nodejs-logging-bunyan/compare/v3.1.0...v3.1.1) (2021-09-08)
+
+
+### Bug Fixes
+
+* **build:** update branch to main ([#560](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/560)) ([4616ad9](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/4616ad9ccbd592f694b33b701eb945146b2fe203))
+
+## [3.1.0](https://www.github.com/googleapis/nodejs-logging-bunyan/compare/v3.0.2...v3.1.0) (2021-06-15)
+
+
+### Features
+
+* add spanId and traceSampled logic ([#543](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/543)) ([548111b](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/548111b87e4817b410cc2d6a13cc468a78aa5f8a))
+
+### [3.0.2](https://www.github.com/googleapis/nodejs-logging-bunyan/compare/v3.0.1...v3.0.2) (2021-02-09)
+
+
+### Bug Fixes
+
+* **deps:** update dependency google-auth-library to v7 ([#513](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/513)) ([07e5830](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/07e5830207f1bbfbe368625c19ee298775403bcb))
+
+### [3.0.1](https://www.github.com/googleapis/nodejs-logging-bunyan/compare/v3.0.0...v3.0.1) (2020-09-12)
+
+
+### Bug Fixes
+
+* **deps:** update dependency yargs to v16 ([#484](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/484)) ([a307179](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/a30717969b0e0cb9726061ff48f4fa7f9b35961e))
+
+## [3.0.0](https://www.github.com/googleapis/nodejs-logging-bunyan/compare/v2.0.3...v3.0.0) (2020-05-20)
+
+
+### ⚠ BREAKING CHANGES
+
+* drop support for node.js 8.x (#440)
+
+### Bug Fixes
+
+* apache license URL ([#468](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/468)) ([#436](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/436)) ([51f5182](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/51f518206f52fdc31773e9625a289f9d25f03abb))
+* update HttpRequest to match @google-cloud/logging ([#412](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/412)) ([0c32a6c](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/0c32a6c5014ac1c4ec54103bd610eae75ef2426a))
+* **deps:** update dependency google-auth-library to v6 ([#427](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/427)) ([03c6c8a](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/03c6c8a9ff9003e906aeea8bec806a87fadef2c3))
+
+
+### Build System
+
+* drop support for node.js 8.x ([#440](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/440)) ([b816566](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/b81656645e7a17adfdb1fb78de8e2153128fe2a5))
+
+### [2.0.3](https://www.github.com/googleapis/nodejs-logging-bunyan/compare/v2.0.2...v2.0.3) (2020-01-24)
+
+
+### Bug Fixes
+
+* **docs:** bump release level to GA ([#378](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/378)) ([2d99634](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/2d99634ef4950bea81c64f1355b59ace3138b63e))
+
+### [2.0.2](https://www.github.com/googleapis/nodejs-logging-bunyan/compare/v2.0.1...v2.0.2) (2019-12-05)
+
+
+### Bug Fixes
+
+* **deps:** TypeScript 3.7.0 causes breaking change in typings ([#384](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/384)) ([b7c509c](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/b7c509ce00d8436405abe4e19922594825927a13))
+
+### [2.0.1](https://www.github.com/googleapis/nodejs-logging-bunyan/compare/v2.0.0...v2.0.1) (2019-12-02)
+
+
+### Bug Fixes
+
+* **deps:** update dependency yargs to v15 ([#377](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/377)) ([730bac9](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/730bac9fb1b16575318a0eb167d3bba550318704))
+* **docs:** snippets are now replaced in jsdoc comments ([#371](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/371)) ([1cfbf8d](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/1cfbf8deff023c09075d319904a267f13febbaeb))
+
+## [2.0.0](https://www.github.com/googleapis/nodejs-logging-bunyan/compare/v1.2.3...v2.0.0) (2019-10-18)
+
+
+### ⚠ BREAKING CHANGES
+
+* truncate log messages > 250,000 bytes (#365)
+
+### Features
+
+* truncate log messages > 250,000 bytes ([#365](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/365)) ([b712f12](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/b712f123e975b94cd3f096a0cdaf05951320ffac))
+
+### [1.2.3](https://www.github.com/googleapis/nodejs-logging-bunyan/compare/v1.2.2...v1.2.3) (2019-09-03)
+
+
+### Bug Fixes
+
+* **deps:** update dependency yargs to v14 ([#346](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/346)) ([93f7deb](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/93f7deb))
+* **docs:** remove anchor from reference doc link ([#348](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/348)) ([6937d45](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/6937d45))
+
+### [1.2.2](https://www.github.com/googleapis/nodejs-logging-bunyan/compare/v1.2.1...v1.2.2) (2019-08-13)
+
+
+### Bug Fixes
+
+* **deps:** update dependency google-auth-library to v5 ([#342](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/342)) ([f560753](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/f560753))
+
+### [1.2.1](https://www.github.com/googleapis/nodejs-logging-bunyan/compare/v1.2.0...v1.2.1) (2019-06-26)
+
+
+### Bug Fixes
+
+* **docs:** link to reference docs section on googleapis.dev ([#338](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/338)) ([c576ae4](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/c576ae4))
+
+## [1.2.0](https://www.github.com/googleapis/nodejs-logging-bunyan/compare/v1.1.1...v1.2.0) (2019-06-24)
+
+
+### Features
+
+* add support for apiEndpoint override ([#336](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/336)) ([5feb3c9](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/5feb3c9))
+
+### [1.1.1](https://www.github.com/googleapis/nodejs-logging-bunyan/compare/v1.1.0...v1.1.1) (2019-06-14)
+
+
+### Bug Fixes
+
+* **docs:** move to new client docs URL ([#331](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/331)) ([644f80c](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/644f80c))
+* should read logging Bunyan not Winston ([#330](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/330)) ([d08c03f](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/d08c03f))
+
+## [1.1.0](https://www.github.com/googleapis/nodejs-logging-bunyan/compare/v1.0.0...v1.1.0) (2019-06-05)
+
+
+### Features
+
+* add .repo-metadata.json, generate docs ([#328](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/328)) ([342a41e](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/342a41e))
+
+## [1.0.0](https://www.github.com/googleapis/nodejs-logging-bunyan/compare/v0.10.1...v1.0.0) (2019-05-29)
+
+
+### ⚠ BREAKING CHANGES
+
+* upgrade engines field to >=8.10.0 (#298)
+
+### Bug Fixes
+
+* proper signature for _write[v] ([#287](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/287)) ([8bb305a](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/8bb305a))
+* **deps:** update dependency google-auth-library to v4 ([#308](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/308)) ([e309b7c](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/e309b7c))
+
+
+### Build System
+
+* upgrade engines field to >=8.10.0 ([#298](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/298)) ([143933c](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/143933c))
+
+
+### Features
+
+* auto-detect service context ([#290](https://www.github.com/googleapis/nodejs-logging-bunyan/issues/290)) ([595a4db](https://www.github.com/googleapis/nodejs-logging-bunyan/commit/595a4db))
+
+## v0.10.1
+
+03-14-2019 14:58 PDT
+
+### Bug Fixes
+- fix: add missing dep on google-auth-library ([#278](https://github.com/googleapis/nodejs-logging-bunyan/pull/278))
+
+## v0.10.0
+
+03-12-2019 16:24 PDT
+
+### New Features
+- feat(middleware): generate parent request logs ([#235](https://github.com/googleapis/nodejs-logging-bunyan/pull/235))
+
+### Bug Fixes
+- fix: do not promote record.labels if improper ([#266](https://github.com/googleapis/nodejs-logging-bunyan/pull/266))
+- fix: remove deep dependency into auth-library ([#251](https://github.com/googleapis/nodejs-logging-bunyan/pull/251))
+
+### Documentation
+- docs: update links in contrib guide ([#263](https://github.com/googleapis/nodejs-logging-bunyan/pull/263))
+- docs: update contributing path in README ([#256](https://github.com/googleapis/nodejs-logging-bunyan/pull/256))
+- docs: move CONTRIBUTING.md to root ([#255](https://github.com/googleapis/nodejs-logging-bunyan/pull/255))
+- docs: add lint/fix example to contributing guide ([#252](https://github.com/googleapis/nodejs-logging-bunyan/pull/252))
+- docs: use https for links ([#246](https://github.com/googleapis/nodejs-logging-bunyan/pull/246))
+
+### Internal / Testing Changes
+- chore: allow custom port in samples tests ([#272](https://github.com/googleapis/nodejs-logging-bunyan/pull/272))
+- build: Add docuploader credentials to node publish jobs ([#270](https://github.com/googleapis/nodejs-logging-bunyan/pull/270))
+- build: update release config ([#268](https://github.com/googleapis/nodejs-logging-bunyan/pull/268))
+- build: use node10 to run samples-test, system-test etc ([#269](https://github.com/googleapis/nodejs-logging-bunyan/pull/269))
+- chore(deps): update dependency mocha to v6 ([#264](https://github.com/googleapis/nodejs-logging-bunyan/pull/264))
+- build: use linkinator for docs test ([#262](https://github.com/googleapis/nodejs-logging-bunyan/pull/262))
+- fix: de-flake system tests ([#261](https://github.com/googleapis/nodejs-logging-bunyan/pull/261))
+- chore: resolve TODO in test ([#259](https://github.com/googleapis/nodejs-logging-bunyan/pull/259))
+- build: create docs test npm scripts ([#258](https://github.com/googleapis/nodejs-logging-bunyan/pull/258))
+- build: test using @grpc/grpc-js in CI ([#257](https://github.com/googleapis/nodejs-logging-bunyan/pull/257))
+- chore(deps): update dependency @google-cloud/common to ^0.31.0 ([#253](https://github.com/googleapis/nodejs-logging-bunyan/pull/253))
+- fix(deps): update dependency yargs to v13 ([#260](https://github.com/googleapis/nodejs-logging-bunyan/pull/260))
+- fix: directly depend on @g-c/common ([#250](https://github.com/googleapis/nodejs-logging-bunyan/pull/250))
+- chore(deps): update dependency eslint-config-prettier to v4 ([#245](https://github.com/googleapis/nodejs-logging-bunyan/pull/245))
+- build: ignore googleapis.com in doc link check ([#242](https://github.com/googleapis/nodejs-logging-bunyan/pull/242))
+- build: check broken links in generated docs ([#239](https://github.com/googleapis/nodejs-logging-bunyan/pull/239))
+- refactor: modernize the sample tests ([#237](https://github.com/googleapis/nodejs-logging-bunyan/pull/237))
+
+## v0.9.5
+
+12-12-2018 19:50 PST
+
+### Bug Fixes
+- fix(middleware): use bunyan log.child ([#163](https://github.com/googleapis/nodejs-logging-bunyan/pull/163))
+- fix: Don't publish sourcemaps ([#166](https://github.com/googleapis/nodejs-logging-bunyan/pull/166))
+
+### Dependencies
+- fix(deps): update dependency @opencensus/propagation-stackdriver to ^0.0.6 ([#192](https://github.com/googleapis/nodejs-logging-bunyan/pull/192))
+- fix(deps): update dependency @opencensus/propagation-stackdriver to ^0.0.5 ([#177](https://github.com/googleapis/nodejs-logging-bunyan/pull/177))
+
+### Docs & Samples
+- docs: update readme badges ([#213](https://github.com/googleapis/nodejs-logging-bunyan/pull/213))
+- refactor: convert samples test from ava to mocha ([#190](https://github.com/googleapis/nodejs-logging-bunyan/pull/190))
+
+### Internal / Testing Changes
+- chore: the sample tests complete correctly ([#232](https://github.com/googleapis/nodejs-logging-bunyan/pull/232))
+- chore(build): inject yoshi automation key ([#231](https://github.com/googleapis/nodejs-logging-bunyan/pull/231))
+- chore: update nyc and eslint configs ([#230](https://github.com/googleapis/nodejs-logging-bunyan/pull/230))
+- chore: fix publish.sh permission +x ([#228](https://github.com/googleapis/nodejs-logging-bunyan/pull/228))
+- fix(build): fix Kokoro release script ([#227](https://github.com/googleapis/nodejs-logging-bunyan/pull/227))
+- build: add Kokoro configs for autorelease ([#226](https://github.com/googleapis/nodejs-logging-bunyan/pull/226))
+- chore: always nyc report before calling codecov ([#222](https://github.com/googleapis/nodejs-logging-bunyan/pull/222))
+- chore: nyc ignore build/test by default ([#220](https://github.com/googleapis/nodejs-logging-bunyan/pull/220))
+- refactor: reduce the number of dependencies ([#206](https://github.com/googleapis/nodejs-logging-bunyan/pull/206))
+- chore: clean up usage of prettier and eslint ([#219](https://github.com/googleapis/nodejs-logging-bunyan/pull/219))
+- chore: update system tests key ([#216](https://github.com/googleapis/nodejs-logging-bunyan/pull/216))
+- chore: add polling to system tests ([#217](https://github.com/googleapis/nodejs-logging-bunyan/pull/217))
+- chore: update license file ([#215](https://github.com/googleapis/nodejs-logging-bunyan/pull/215))
+- fix(build): fix system key decryption ([#211](https://github.com/googleapis/nodejs-logging-bunyan/pull/211))
+- chore(deps): update dependency typescript to ~3.2.0 ([#209](https://github.com/googleapis/nodejs-logging-bunyan/pull/209))
+- chore: update system tests key ([#210](https://github.com/googleapis/nodejs-logging-bunyan/pull/210))
+- chore: add synth.metadata
+- chore(deps): update dependency gts to ^0.9.0 ([#202](https://github.com/googleapis/nodejs-logging-bunyan/pull/202))
+- chore: update eslintignore config ([#201](https://github.com/googleapis/nodejs-logging-bunyan/pull/201))
+- chore(deps): update dependency @google-cloud/nodejs-repo-tools to v3 ([#200](https://github.com/googleapis/nodejs-logging-bunyan/pull/200))
+- chore: drop contributors from multiple places ([#199](https://github.com/googleapis/nodejs-logging-bunyan/pull/199))
+- refactor(middleware): use common code from logging ([#197](https://github.com/googleapis/nodejs-logging-bunyan/pull/197))
+- cleanup: remove unnecessary deps and files ([#198](https://github.com/googleapis/nodejs-logging-bunyan/pull/198))
+- chore: use latest npm on Windows ([#196](https://github.com/googleapis/nodejs-logging-bunyan/pull/196))
+- chore: update CircleCI config ([#195](https://github.com/googleapis/nodejs-logging-bunyan/pull/195))
+- chore: include build in eslintignore ([#191](https://github.com/googleapis/nodejs-logging-bunyan/pull/191))
+- chore(deps): update dependency eslint-plugin-node to v8 ([#186](https://github.com/googleapis/nodejs-logging-bunyan/pull/186))
+- fix(deps): update dependency @google-cloud/common to ^0.26.0 ([#181](https://github.com/googleapis/nodejs-logging-bunyan/pull/181))
+- chore: update issue templates ([#185](https://github.com/googleapis/nodejs-logging-bunyan/pull/185))
+- chore: remove old issue template ([#183](https://github.com/googleapis/nodejs-logging-bunyan/pull/183))
+- build: run tests on node11 ([#182](https://github.com/googleapis/nodejs-logging-bunyan/pull/182))
+- chores(build): do not collect sponge.xml from windows builds ([#180](https://github.com/googleapis/nodejs-logging-bunyan/pull/180))
+- chores(build): run codecov on continuous builds ([#179](https://github.com/googleapis/nodejs-logging-bunyan/pull/179))
+- chore: update new issue template ([#178](https://github.com/googleapis/nodejs-logging-bunyan/pull/178))
+- build: fix codecov uploading on Kokoro ([#174](https://github.com/googleapis/nodejs-logging-bunyan/pull/174))
+- Update kokoro config ([#171](https://github.com/googleapis/nodejs-logging-bunyan/pull/171))
+- chore(deps): update dependency eslint-plugin-prettier to v3 ([#170](https://github.com/googleapis/nodejs-logging-bunyan/pull/170))
+- chore(deps): update dependency typescript to ~3.1.0 ([#169](https://github.com/googleapis/nodejs-logging-bunyan/pull/169))
+
+## v0.9.4
+
+### Documentation
+- fix: doc string is malformed for jsdoc ([#164](https://github.com/googleapis/nodejs-logging-bunyan/pull/164))
+
+## v0.9.2
+
+### Bug Fixes
+- fix: logged errors are reported to error reporting ([#122](https://github.com/googleapis/nodejs-logging-bunyan/pull/122))
+- chore: fix `stream` example in README ([#134](https://github.com/googleapis/nodejs-logging-bunyan/pull/134))
+- fix(deps): update @google-cloud/logging to 4.x ([#152](https://github.com/googleapis/nodejs-logging-bunyan/pull/152))
+
+### Dependencies
+- fix(deps): update dependency @google-cloud/common to ^0.25.0 ([#150](https://github.com/googleapis/nodejs-logging-bunyan/pull/150))
+- chore(deps): update dependency delay to v4 ([#142](https://github.com/googleapis/nodejs-logging-bunyan/pull/142))
+- fix(deps): update dependency @google-cloud/common to ^0.24.0 ([#146](https://github.com/googleapis/nodejs-logging-bunyan/pull/146))
+- fix(deps): update dependency @opencensus/propagation-stackdriver to ^0.0.4 ([#141](https://github.com/googleapis/nodejs-logging-bunyan/pull/141))
+- fix(deps): update dependency @google-cloud/common to ^0.23.0 ([#139](https://github.com/googleapis/nodejs-logging-bunyan/pull/139))
+- fix(deps): update samples dependency @google-cloud/logging-bunyan to ^0.9.0 ([#137](https://github.com/googleapis/nodejs-logging-bunyan/pull/137))
+- chore(deps): update dependency execa to v1 ([#138](https://github.com/googleapis/nodejs-logging-bunyan/pull/138))
+
+### Internal / Testing Changes
+- Enable prefer-const in the eslint config ([#151](https://github.com/googleapis/nodejs-logging-bunyan/pull/151))
+- Enable no-var in eslint ([#149](https://github.com/googleapis/nodejs-logging-bunyan/pull/149))
+- Update CI config ([#147](https://github.com/googleapis/nodejs-logging-bunyan/pull/147))
+- Add synth script and update CI ([#144](https://github.com/googleapis/nodejs-logging-bunyan/pull/144))
+- Retry npm install in CI ([#143](https://github.com/googleapis/nodejs-logging-bunyan/pull/143))
+- chore(deps): update dependency nyc to v13 ([#140](https://github.com/googleapis/nodejs-logging-bunyan/pull/140))
+
+## v0.9.1
+
+### Fixes
+- chore: fix install tests (#133)
+
+## v0.9.0
+
+### Breaking changes
+- fix: drop support for node.js 4.x and 9.x (#87)
+- fix: drop support for node 4.x and 9.x (#69)
+
+### Features
+- feat: request-correlating middleware (#63)
+
+### Docs & Samples
+- chore: require node 8 for samples (#107)
+- doc: add express middleware to README (#97)
+- doc: fix typo in samples/README (#99)
+- fix: fix linting errors in the samples (#100)
+- feat(samples): add express middleware sample (#95)
+- fix(samples): fix non-working explit setup sample (#93)
+- doc: fix usage of logger.info in README.md (#83)
+- doc: fix link to HttpRequest message (#68)
+
+### Dependency updates
+- chore(deps): upgrade to @g-c/logging@3.0.1 (#126)
+- chore: pin to delay@3.0.x (#127)
+- chore(deps): update dependency execa to ^0.11.0 (#125)
+- chore(deps): update dependency eslint-config-prettier to v3 (#120)
+- chore(deps): update dependency pify to v4 (#119)
+- chore(deps): update dependency got to v9 (#114)
+- fix(deps): update dependency @opencensus/propagation-stackdriver to ^0.0.3 (#112)
+- chore(deps): update dependency typescript to v3 (#109)
+- chore(deps): update dependency eslint-plugin-node to v7 (#96)
+- chore(deps): update dependency gts to ^0.8.0 (#92)
+- fix(deps): update dependency @google-cloud/logging to v2 (#86)
+- fix(deps): update dependency yargs to v12 (#82)
+- chore: update dependencies (#81)
+- Configure Renovate (#74)
+- chore(package): missing @types/node dev dependency (#75)
+- chore: update all dependencies (#71)
+- chore(package): upgrade gts and typescript (#70)
+
+### Keepin' the lights on
+- chore: ignore package-lock.json (#118)
+- chore: update renovate config (#113)
+- remove that whitespace (#111)
+- chore: assert.deelEqual => assert.deepStrictEqual (#108)
+- chore: move mocha options to mocha.opts (#106)
+- chore: re-enable codecov && drop greenkeeper badge (#98)
+- refactor: drop repo-tool as an exec wrapper (#79)
+- chore: update sample lockfiles (#78)
+- fix: update linking for samples (#77)
+- cleanup: remove some casts (#76)
+- fix: the system tests use a custom log (#73)
+- test: use source-map-support (#72)
+- chore: remove `--bail` from the system tests config (#67)
+- chore: the ultimate fix for repo-tools EPERM (#64)
diff --git a/handwritten/nodejs-logging-bunyan/CODE_OF_CONDUCT.md b/handwritten/nodejs-logging-bunyan/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000000..2add2547a81
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/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-bunyan/CONTRIBUTING.md b/handwritten/nodejs-logging-bunyan/CONTRIBUTING.md
new file mode 100644
index 00000000000..d9f72ea43ba
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/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 Bunyan 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-bunyan/LICENSE b/handwritten/nodejs-logging-bunyan/LICENSE
new file mode 100644
index 00000000000..d6456956733
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/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-bunyan/README.md b/handwritten/nodejs-logging-bunyan/README.md
new file mode 100644
index 00000000000..47e3b1c270a
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/README.md
@@ -0,0 +1,349 @@
+[//]: # "This README.md file is auto-generated, all changes to this file will be lost."
+[//]: # "To regenerate it, use `python -m synthtool`."
+
+
+# [Cloud Logging for Bunyan: Node.js Client](https://github.com/googleapis/nodejs-logging-bunyan)
+
+[](https://cloud.google.com/terms/launch-stages)
+[](https://www.npmjs.org/package/@google-cloud/logging-bunyan)
+
+
+
+
+This module provides an easy to use, higher-level layer for working with [Cloud Logging](https://cloud.google.com/logging/docs),
+compatible with [Bunyan](https://www.npmjs.com/package/bunyan). Simply attach this as a transport to your existing Bunyan loggers.
+
+
+A comprehensive list of changes in each version may be found in
+[the CHANGELOG](https://github.com/googleapis/nodejs-logging-bunyan/blob/main/CHANGELOG.md).
+
+* [Cloud Logging for Bunyan Node.js Client API Reference][client-docs]
+* [Cloud Logging for Bunyan Documentation][product-docs]
+* [github.com/googleapis/nodejs-logging-bunyan](https://github.com/googleapis/nodejs-logging-bunyan)
+
+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 Bunyan 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-bunyan
+```
+
+
+### Using the client library
+
+```javascript
+const bunyan = require('bunyan');
+
+// Imports the Google Cloud client library for Bunyan
+const {LoggingBunyan} = require('@google-cloud/logging-bunyan');
+
+// Creates a Bunyan Cloud Logging client
+const loggingBunyan = new LoggingBunyan();
+
+// Create a Bunyan logger that streams to Cloud Logging
+// Logs will be written to: "projects/YOUR_PROJECT_ID/logs/bunyan_log"
+const logger = bunyan.createLogger({
+ // The JSON payload of the log as it appears in Cloud Logging
+ // will contain "name": "my-service"
+ name: 'my-service',
+ streams: [
+ // Log to the console at 'info' and above
+ {stream: process.stdout, level: 'info'},
+ // And log to Cloud Logging, logging at 'info' and above
+ loggingBunyan.stream('info'),
+ ],
+});
+
+// Writes some log entries
+logger.error('warp nacelles offline');
+logger.info('shields at 99%');
+
+```
+### 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:
+
+
+
+The middleware adds a `bunyan`-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 lb = require('@google-cloud/logging-bunyan');
+
+// Import express module and create an http server.
+const express = require('express');
+
+async function startServer() {
+ const {logger, mw} = await lb.express.middleware();
+ const app = express();
+
+ // Install the logging middleware. This ensures that a Bunyan-style `log`
+ // function is available on the `request` object. Attach this as one of the
+ // earliest middleware to make sure that log function is available in all the
+ // 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 bunyan 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 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({port: 8080}, 'bonjour');
+
+ // Start listening on the http server.
+ app.listen(8080, () => {
+ console.log('http server listening on port 8080');
+ });
+}
+
+startServer();
+```
+
+### Error Reporting
+
+Any `Error` objects you log at severity `error` or higher can automatically be picked up by [Cloud Error Reporting](https://cloud.google.com/error-reporting/) if you have specified a `serviceContext.service` when instantiating a `LoggingBunyan` instance:
+
+```javascript
+const loggingBunyan = new LoggingBunyan({
+ serviceContext: {
+ service: 'my-service', // required to report logged errors
+ // to the Google Cloud 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][@google-cloud/error-reporting] module which provides direct access to the Error Reporting API.
+
+### Special Payload Fields in LogEntry
+
+There are some fields that are considered special by Google cloud logging and will be extracted into the LogEntry structure. For example, `severity`, `message` and `labels` can be extracted to LogEntry if included in the bunyan log payload. These [special JSON fields](https://cloud.google.com/logging/docs/structured-logging#special-payload-fields) will be used to set the corresponding fields in the `LogEntry`. Please be aware of these special fields to avoid unexpected logging behavior.
+
+### LogEntry Labels
+
+If the bunyan log record contains a label property where all the values are strings, we automatically promote that
+property to be the [`LogEntry.labels`](https://cloud.google.com/logging/docs/reference/v2/rpc/google.logging.v2#logentry) value rather
+than being one of the properties in the `payload` fields. This makes it easier to filter the logs in the UI using the labels.
+
+```javascript
+logger.info({labels: {someKey: 'some value'}}, 'test log message');
+```
+
+All the label values must be strings for this automatic promotion to work. Otherwise the labels are left in the payload.
+
+### Formatting Request Logs
+
+To format your request logs you can provide a `httpRequest` property on the bunyan metadata you provide along with the log message. 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:
+
+
+
+```js
+logger.info({
+ httpRequest: {
+ status: res.statusCode,
+ requestUrl: req.url,
+ requestMethod: req.method,
+ remoteIp: req.connection.remoteAddress,
+ // etc.
+ }
+}, req.path);
+```
+
+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: the linked protobuf documentation shows `snake_case` property names, but in JavaScript one needs to provide property names in `camelCase`.)
+
+### Correlating Logs with Traces
+
+If you use [@google-cloud/trace-agent][trace-agent] module, then this module will set the Cloud Logging [LogEntry][LogEntry] `trace` property based on the current trace context when available. That correlation allows you to [view log entries][trace-viewing-log-entries] inline with trace spans in the Cloud Trace Viewer. Example:
+
+
+
+If you wish to set the Cloud LogEntry `trace` property with a custom value, then write a Bunyan log entry property for `'logging.googleapis.com/trace'`, which is exported by this module as `LOGGING_TRACE_KEY`. For example:
+
+```js
+const bunyan = require('bunyan');
+// Node 6+
+const {LoggingBunyan, LOGGING_TRACE_KEY} = require('@google-cloud/logging-bunyan');
+const loggingBunyan = LoggingBunyan();
+
+...
+
+logger.info({
+ [LOGGING_TRACE_KEY]: 'custom-trace-value'
+}, 'Bunyan log entry with custom trace field');
+```
+
+### Error handling with a default callback
+The `LoggingBunyan` class creates an instance of `Logging` which creates 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 when it fails or an error is returned from Logging backend.
+If the error is not handled, it could crash the application. One possible way to handle the error is to provide a default callback
+to the `LoggingBunyan` constructor which will be used to initialize the `Log` object with that callback like in the example below:
+
+```js
+// Imports the Google Cloud client library for Bunyan
+const {LoggingBunyan} = require('@google-cloud/logging-bunyan');
+// Creates a client
+const loggingBunyan = new LoggingBunyan({
+ projectId: 'your-project-id',
+ keyFilename: '/path/to/key.json',
+ defaultCallback: err => {
+ if (err) {
+ console.log('Error occured: ' + err);
+ }
+ },
+});
+```
+
+### 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 `LoggingBunyan` 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 Bunyan
+const {LoggingBunyan} = require('@google-cloud/logging-bunyan');
+
+// Creates a client
+const loggingBunyan = new LoggingBunyan({
+ projectId: 'your-project-id',
+ keyFilename: '/path/to/key.json',
+ redirectToStdout: true,
+});
+```
+
+
+## Samples
+
+Samples are in the [`samples/`](https://github.com/googleapis/nodejs-logging-bunyan/tree/main/samples) directory. Each sample's `README.md` has instructions for running its sample.
+
+| Sample | Source Code | Try it |
+| --------------------------- | --------------------------------- | ------ |
+| Express | [source code](https://github.com/googleapis/nodejs-logging-bunyan/blob/main/samples/express.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-logging-bunyan&page=editor&open_in_editor=samples/express.js,samples/README.md) |
+| Quickstart | [source code](https://github.com/googleapis/nodejs-logging-bunyan/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-bunyan&page=editor&open_in_editor=samples/quickstart.js,samples/README.md) |
+| Explict Auth Setup | [source code](https://github.com/googleapis/nodejs-logging-bunyan/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-bunyan&page=editor&open_in_editor=samples/setup_explicit.js,samples/README.md) |
+
+
+
+The [Cloud Logging for Bunyan 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-bunyan@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-bunyan/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-bunyan/blob/main/LICENSE)
+
+[client-docs]: https://cloud.google.com/nodejs/docs/reference/logging-bunyan/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-bunyan/doc/images/bunyan-logs-in-trace.png b/handwritten/nodejs-logging-bunyan/doc/images/bunyan-logs-in-trace.png
new file mode 100644
index 00000000000..66a21325bba
Binary files /dev/null and b/handwritten/nodejs-logging-bunyan/doc/images/bunyan-logs-in-trace.png differ
diff --git a/handwritten/nodejs-logging-bunyan/doc/images/request-bundling.png b/handwritten/nodejs-logging-bunyan/doc/images/request-bundling.png
new file mode 100644
index 00000000000..e81e2be34bc
Binary files /dev/null and b/handwritten/nodejs-logging-bunyan/doc/images/request-bundling.png differ
diff --git a/handwritten/nodejs-logging-bunyan/doc/images/request-log.png b/handwritten/nodejs-logging-bunyan/doc/images/request-log.png
new file mode 100644
index 00000000000..a58853afa15
Binary files /dev/null and b/handwritten/nodejs-logging-bunyan/doc/images/request-log.png differ
diff --git a/handwritten/nodejs-logging-bunyan/linkinator.config.json b/handwritten/nodejs-logging-bunyan/linkinator.config.json
new file mode 100644
index 00000000000..29a223b6db6
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/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-bunyan/owlbot.py b/handwritten/nodejs-logging-bunyan/owlbot.py
new file mode 100644
index 00000000000..3d2b1f5cb8c
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/owlbot.py
@@ -0,0 +1,52 @@
+# 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 os
+import synthtool as s
+import synthtool.gcp as gcp
+import synthtool.languages.node_mono_repo as node
+import logging
+
+logging.basicConfig(level=logging.DEBUG)
+
+
+common_templates = gcp.CommonTemplates()
+templates = common_templates.node_library(source_location='build/src')
+s.move(templates, excludes=[
+ ".github/auto-label.yaml",
+ ".github/release-please.yml",
+ ".github/CODEOWNERS",
+ ".github/sync-repo-settings.yaml",
+])
+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-bunyan/package.json b/handwritten/nodejs-logging-bunyan/package.json
new file mode 100644
index 00000000000..790243df625
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/package.json
@@ -0,0 +1,90 @@
+{
+ "name": "@google-cloud/logging-bunyan",
+ "description": "Cloud Logging stream for Bunyan",
+ "version": "5.1.0",
+ "license": "Apache-2.0",
+ "author": "Google Inc.",
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "repository": {
+ "type": "git",
+ "directory": "handwritten/nodejs-logging-bunyan",
+ "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"
+ ],
+ "keywords": [
+ "google apis client",
+ "google api client",
+ "google apis",
+ "google api",
+ "google",
+ "google cloud platform",
+ "google cloud",
+ "cloud",
+ "google logging",
+ "logging",
+ "stackdriver logging",
+ "stackdriver",
+ "bunyan stream",
+ "winston"
+ ],
+ "scripts": {
+ "predocs": "npm run compile",
+ "docs": "jsdoc -c .jsdoc.js",
+ "lint": "gts check",
+ "presamples-test": "npm run compile",
+ "presystem-test": "npm run compile",
+ "samples-test": "cd samples/ && npm link ../ && npm test && cd ../",
+ "system-test": "mocha build/system-test --timeout 600000",
+ "test": "c8 mocha --recursive build/test",
+ "clean": "gts clean",
+ "compile": "tsc -p .",
+ "postcompile": "cpy \"src/types/*\" build/src/types",
+ "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.0.0",
+ "google-auth-library": "^9.0.0"
+ },
+ "devDependencies": {
+ "@google-cloud/common": "^5.0.0",
+ "@types/bunyan": "^1.8.4",
+ "@types/express": "^4.16.0",
+ "@types/mocha": "^9.0.0",
+ "@types/node": "^20.4.9",
+ "@types/proxyquire": "^1.3.28",
+ "@types/uuid": "^10.0.0",
+ "bunyan": "^1.8.12",
+ "c8": "^9.0.0",
+ "codecov": "^3.0.2",
+ "cpy-cli": "^4.0.0",
+ "delay": "^5.0.0",
+ "express": "^4.16.3",
+ "gts": "^5.0.0",
+ "jsdoc": "^4.0.0",
+ "jsdoc-fresh": "^3.0.0",
+ "jsdoc-region-tag": "^3.0.0",
+ "linkinator": "^3.0.0",
+ "mocha": "^9.2.2",
+ "post-install-check": "0.0.1",
+ "proxyquire": "^2.0.1",
+ "typescript": "^5.1.6",
+ "uuid": "^10.0.0"
+ },
+ "peerDependencies": {
+ "bunyan": "*"
+ },
+ "homepage": "https://github.com/googleapis/google-cloud-node/tree/main/handwritten/nodejs-logging-bunyan"
+}
diff --git a/handwritten/nodejs-logging-bunyan/samples/.eslintrc.yml b/handwritten/nodejs-logging-bunyan/samples/.eslintrc.yml
new file mode 100644
index 00000000000..282535f55f6
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/samples/.eslintrc.yml
@@ -0,0 +1,3 @@
+---
+rules:
+ no-console: off
diff --git a/handwritten/nodejs-logging-bunyan/samples/README.md b/handwritten/nodejs-logging-bunyan/samples/README.md
new file mode 100644
index 00000000000..c05b0a96bb5
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/samples/README.md
@@ -0,0 +1,87 @@
+[//]: # "This README.md file is auto-generated, all changes to this file will be lost."
+[//]: # "To regenerate it, use `python -m synthtool`."
+
+
+# [Cloud Logging for Bunyan: Node.js Samples](https://github.com/googleapis/nodejs-logging-bunyan)
+
+[![Open in Cloud Shell][shell_img]][shell_link]
+
+This module provides an easy to use, higher-level layer for working with [Cloud Logging](https://cloud.google.com/logging/docs),
+compatible with [Bunyan](https://www.npmjs.com/package/bunyan). Simply attach this as a transport to your existing Bunyan loggers.
+
+## Table of Contents
+
+* [Before you begin](#before-you-begin)
+* [Samples](#samples)
+ * [Express](#express)
+ * [Quickstart](#quickstart)
+ * [Explict Auth Setup](#explict-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-bunyan#using-the-client-library).
+
+`cd samples`
+
+`npm install`
+
+`cd ..`
+
+## Samples
+
+
+
+### Express
+
+View the [source code](https://github.com/googleapis/nodejs-logging-bunyan/blob/main/samples/express.js).
+
+[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-logging-bunyan&page=editor&open_in_editor=samples/express.js,samples/README.md)
+
+__Usage:__
+
+
+`node samples/express.js`
+
+
+-----
+
+
+
+
+### Quickstart
+
+View the [source code](https://github.com/googleapis/nodejs-logging-bunyan/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-bunyan&page=editor&open_in_editor=samples/quickstart.js,samples/README.md)
+
+__Usage:__
+
+
+`node samples/quickstart.js`
+
+
+-----
+
+
+
+
+### Explict Auth Setup
+
+View the [source code](https://github.com/googleapis/nodejs-logging-bunyan/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-bunyan&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-bunyan&page=editor&open_in_editor=samples/README.md
+[product-docs]: https://cloud.google.com/logging
diff --git a/handwritten/nodejs-logging-bunyan/samples/express.js b/handwritten/nodejs-logging-bunyan/samples/express.js
new file mode 100644
index 00000000000..2c7d4e28a4f
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/samples/express.js
@@ -0,0 +1,63 @@
+// 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.
+
+'use strict';
+
+// [START logging_bunyan_express]
+// Imports the Google Cloud client library for Bunyan.
+const lb = require('@google-cloud/logging-bunyan');
+
+// Import express module and create an http server.
+const express = require('express');
+
+async function startServer() {
+ const {logger, mw} = await lb.express.middleware({
+ logName: 'samples_express',
+ });
+ const app = express();
+
+ // Install the logging middleware. This ensures that a Bunyan-style `log`
+ // function is available on the `request` object. This should be the very
+ // first middleware you attach to your app.
+ app.use(mw);
+
+ // Setup an http route and a route handler.
+ app.get('/', (req, res) => {
+ // `req.log` can be used as a bunyan 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 Stackdriver
+ // UI.
+ req.log.info('this is an info log message');
+ res.send('hello world');
+ });
+
+ const port = process.env.PORT || 8080;
+
+ // `logger` can be used as a global logger, one not correlated to any specific
+ // request.
+ logger.info({port}, 'bonjour');
+
+ // Start listening on the http server.
+ const server = app.listen(port, () => {
+ console.log(`http server listening on port ${port}`);
+ });
+
+ app.get('/shutdown', (req, res) => {
+ res.sendStatus(200);
+ server.close();
+ });
+}
+
+startServer();
+// [END logging_bunyan_express]
diff --git a/handwritten/nodejs-logging-bunyan/samples/package.json b/handwritten/nodejs-logging-bunyan/samples/package.json
new file mode 100644
index 00000000000..8281c8de5e4
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/samples/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "nodejs-docs-samples-logging-bunyan",
+ "files": [
+ "*.js",
+ "!test/"
+ ],
+ "private": true,
+ "license": "Apache-2.0",
+ "author": "Google Inc.",
+ "repository": "googleapis/nodejs-logging-bunyan",
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "scripts": {
+ "test": "mocha system-test --timeout 600000"
+ },
+ "dependencies": {
+ "@google-cloud/logging-bunyan": "^5.1.0",
+ "bunyan": "^1.8.12",
+ "express": "^4.16.3",
+ "yargs": "^16.0.0"
+ },
+ "devDependencies": {
+ "@google-cloud/logging": "^11.0.0",
+ "chai": "^4.2.0",
+ "delay": "^5.0.0",
+ "got": "^11.0.0",
+ "mocha": "^8.0.0"
+ }
+}
\ No newline at end of file
diff --git a/handwritten/nodejs-logging-bunyan/samples/quickstart.js b/handwritten/nodejs-logging-bunyan/samples/quickstart.js
new file mode 100644
index 00000000000..6f58c7fde2a
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/samples/quickstart.js
@@ -0,0 +1,43 @@
+// Copyright 2017 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_bunyan_quickstart]
+const bunyan = require('bunyan');
+
+// Imports the Google Cloud client library for Bunyan
+const {LoggingBunyan} = require('@google-cloud/logging-bunyan');
+
+// Creates a Bunyan Cloud Logging client
+const loggingBunyan = new LoggingBunyan();
+
+// Create a Bunyan logger that streams to Cloud Logging
+// Logs will be written to: "projects/YOUR_PROJECT_ID/logs/bunyan_log"
+const logger = bunyan.createLogger({
+ // The JSON payload of the log as it appears in Cloud Logging
+ // will contain "name": "my-service"
+ name: 'my-service',
+ streams: [
+ // Log to the console at 'info' and above
+ {stream: process.stdout, level: 'info'},
+ // And log to Cloud Logging, logging at 'info' and above
+ loggingBunyan.stream('info'),
+ ],
+});
+
+// Writes some log entries
+logger.error('warp nacelles offline');
+logger.info('shields at 99%');
+// [END logging_bunyan_quickstart]
diff --git a/handwritten/nodejs-logging-bunyan/samples/setup_explicit.js b/handwritten/nodejs-logging-bunyan/samples/setup_explicit.js
new file mode 100644
index 00000000000..4010751d092
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/samples/setup_explicit.js
@@ -0,0 +1,31 @@
+// Copyright 2017 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';
+
+// sample-metadata:
+// title: Explict Auth Setup
+
+/* eslint-disable no-unused-vars */
+// [START logging_bunyan_setup_explicit]
+// Imports the Google Cloud client library for Bunyan
+const {LoggingBunyan} = require('@google-cloud/logging-bunyan');
+
+// Creates a client
+const loggingBunyan = new LoggingBunyan({
+ projectId: 'your-project-id',
+ keyFilename: '/path/to/key.json',
+});
+// [END logging_bunyan_setup_explicit]
+/* eslint-enable no-unused-vars */
diff --git a/handwritten/nodejs-logging-bunyan/samples/system-test/express.test.js b/handwritten/nodejs-logging-bunyan/samples/system-test/express.test.js
new file mode 100644
index 00000000000..d33a968a7f1
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/samples/system-test/express.test.js
@@ -0,0 +1,62 @@
+// Copyright 2017 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, before, after} = require('mocha');
+const {spawn} = require('child_process');
+const delay = require('delay');
+const got = require('got');
+const {Logging} = require('@google-cloud/logging');
+const logging = new Logging();
+
+const PORT = process.env.PORT || 8080;
+const lb = require('@google-cloud/logging-bunyan');
+const {APP_LOG_SUFFIX} = lb.express;
+
+describe('express samples', () => {
+ after(() => got(`http://localhost:${PORT}/shutdown`));
+ before(async () => {
+ // Start the express server.
+ spawn(process.execPath, ['express.js'], {
+ cwd: path.join(__dirname, '..'),
+ cleanup: true, // kill child process when parent exits.
+ }).stdout.pipe(process.stdout);
+
+ // Wait 10 seconds for initialization and for server to start listening.
+ await delay(10 * 1000);
+ });
+
+ it('should write using bunyan', async function () {
+ this.retries(3);
+
+ // Make an HTTP request to exercise a request logging path.
+ await got(`http://localhost:${PORT}/`);
+
+ // Wait 10 seconds for logs to be written to Cloud service.
+ await delay(10 * 1000);
+
+ // Make sure the log was written to Cloud Logging.
+ const log = logging.log(`samples_express_${APP_LOG_SUFFIX}`);
+ const entries = (await log.getEntries({pageSize: 1}))[0];
+ assert.strictEqual(entries.length, 1);
+ const entry = entries[0];
+ assert.strictEqual('this is an info log message', entry.data.message);
+ // Ensure that a functional logger ws configured with the sample:
+ assert.ok(entry.metadata.trace, 'should have a trace property');
+ assert.match(entry.metadata.trace, /projects\/.*\/traces\/.*/);
+ });
+});
diff --git a/handwritten/nodejs-logging-bunyan/samples/system-test/quickstart.test.js b/handwritten/nodejs-logging-bunyan/samples/system-test/quickstart.test.js
new file mode 100644
index 00000000000..fc1d4330939
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/samples/system-test/quickstart.test.js
@@ -0,0 +1,31 @@
+// Copyright 2017 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'});
+
+const cwd = path.join(__dirname, '..');
+
+describe('quickstart samples', () => {
+ it('should write using bunyan', async () => {
+ const stdout = execSync('node quickstart.js', {cwd});
+ assert.match(stdout, /99%/);
+ });
+});
diff --git a/handwritten/nodejs-logging-bunyan/src/index.ts b/handwritten/nodejs-logging-bunyan/src/index.ts
new file mode 100644
index 00000000000..3269d67ecd0
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/src/index.ts
@@ -0,0 +1,515 @@
+/*!
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * 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 {Writable} from 'stream';
+import * as express from './middleware/express';
+import {
+ setInstrumentationStatus,
+ createDiagnosticEntry,
+} from '@google-cloud/logging/build/src/utils/instrumentation';
+
+// Export the express middleware as 'express'.
+export {express};
+
+import {
+ Logging,
+ detectServiceContext,
+ Log,
+ LogSync,
+ Entry,
+} from '@google-cloud/logging';
+
+import * as types from './types/core';
+
+import {ApiResponseCallback} from '@google-cloud/logging/build/src/log';
+import {LogSeverityFunctions} from '@google-cloud/logging/build/src/utils/log-common';
+import {LogSyncOptions} from '@google-cloud/logging/build/src/log-sync';
+
+// Map of Stackdriver logging levels.
+const BUNYAN_TO_STACKDRIVER: Map = new Map([
+ [60, 'CRITICAL'],
+ [50, 'ERROR'],
+ [40, 'WARNING'],
+ [30, 'INFO'],
+ [20, 'DEBUG'],
+ [10, 'DEBUG'],
+]);
+
+/**
+ * Key to use in the Bunyan payload to allow users to indicate a trace for the
+ * request, and to store as an intermediate value on the log entry before it
+ * gets written to the Cloud Logging logging API.
+ */
+export const LOGGING_TRACE_KEY = 'logging.googleapis.com/trace';
+
+/**
+ * Key to use in the Bunyan payload to allow users to indicate a spanId for the
+ * request, and to store as an intermediate value on the log entry before it
+ * gets written to the Cloud logging API.
+ */
+export const LOGGING_SPAN_KEY = 'logging.googleapis.com/spanId';
+
+/**
+ * Key to use in the Bunyan payload to allow users to indicate a traceSampled
+ * flag for the request, and to store as an intermediate value on the log entry
+ * before it gets written to the Cloud logging API.
+ */
+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_BUNYAN_DEFAULT_LIBRARY_VERSION = '5.1.0'; // {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() {
+ const agent = global._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}`;
+}
+
+/**
+ * This module provides support for streaming your Bunyan logs to
+ * [Stackdriver Logging](https://cloud.google.com/logging).
+ *
+ * @class
+ *
+ * @param {object} [options]
+ * @param {string} [options.logName] The name of the log that will receive
+ * messages written to this bunyan stream. Default: `bunyan_Log`.
+ * @param {object} [options.resource] The monitored resource that the log
+ * stream 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/monitoring/api/ref_v3/rpc/google.api#google.api.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.
+ * @param {constructor} [options.promise] Custom promise module to use instead
+ * of native Promises.
+ * @param {number} [options.maxEntrySize] Max size limit of a log entry.
+ * @param {string[]} [options.jsonFieldsToTruncate] A list of JSON properties at the given full path to be truncated.
+ * @example Import the client library
+ * ```
+ * const {LoggingBunyan} = require('@google-cloud/logging-bunyan');
+ *
+ * ```
+ * @example Create a client that uses Application Default Credentials (ADC):
+ * ```
+ * const loggingBunyan = new
+ * LoggingBunyan();
+ *
+ * ```
+ * @example Create a client with explicit credentials:
+ * ```
+ * const loggingBunyan = new LoggingBunyan({
+ * projectId: 'your-project-id',
+ * keyFilename: '/path/to/keyfile.json'
+ * });
+ *
+ * ```
+ * @example include:samples/quickstart.js
+ * region_tag:logging_bunyan_quickstart
+ * Full quickstart example:
+ *
+ */
+export class LoggingBunyan extends Writable {
+ private logName: string;
+ private resource: types.MonitoredResource | undefined;
+ private serviceContext?: types.ServiceContext;
+ private defaultCallback?: ApiResponseCallback;
+ cloudLog: LogSeverityFunctions;
+ redirectToStdout: boolean;
+
+ constructor(options?: types.Options) {
+ options = options || {};
+ super({objectMode: true});
+ this.logName = options.logName || 'bunyan_log';
+ this.resource = options.resource;
+ this.serviceContext = options.serviceContext;
+ this.defaultCallback = options.defaultCallback;
+ this.redirectToStdout = options.redirectToStdout ?? false;
+ if (!this.redirectToStdout) {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ 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,
+ jsonFieldsToTruncate: options.jsonFieldsToTruncate,
+ });
+ } else {
+ const logSyncOptions: LogSyncOptions = {
+ useMessageField: options.useMessageField ?? true,
+ };
+ this.cloudLog = new Logging(options).logSync(
+ this.logName,
+ undefined,
+ logSyncOptions
+ );
+ }
+
+ // serviceContext.service is required by the Error Reporting
+ // API. Without it, errors that are logged with level 'error'
+ // or higher will not be displayed in the Error Reporting
+ // console.
+ //
+ // As a result, if serviceContext is specified, it is required
+ // that serviceContext.service is specified.
+ if (this.serviceContext && !this.serviceContext.service) {
+ throw new Error(
+ "If 'serviceContext' is specified then " +
+ "'serviceContext.service' is required."
+ );
+ }
+
+ /* Asynchrnously attempt to discover the service context. */
+ if (!this.serviceContext) {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ detectServiceContext((this.cloudLog as any).logging.auth).then(
+ serviceContext => {
+ this.serviceContext = serviceContext!;
+ },
+ () => {
+ /* swallow any errors. */
+ }
+ );
+ }
+ }
+
+ /**
+ * Convenience method that Builds a bunyan stream object that you can put in
+ * the bunyan streams list.
+ */
+ stream(level: types.LogLevel): types.StreamResponse {
+ return {level, type: 'raw', stream: this as Writable};
+ }
+
+ /**
+ * Format a bunyan record into a Stackdriver log entry.
+ */
+ private formatEntry_(record: string | types.BunyanLogRecord) {
+ if (typeof record === 'string') {
+ throw new Error(
+ '@google-cloud/logging-bunyan only works as a raw bunyan stream type.'
+ );
+ }
+ // Stackdriver Log Viewer picks up the summary line from the 'message' field
+ // of the payload. Unless the user has provided a 'message' property also,
+ // move the 'msg' to 'message'.
+ if (!record.message) {
+ // If this is an error, report the full stack trace. This allows
+ // Stackdriver Error Reporting to pick up errors automatically (for
+ // severity 'error' or higher). In this case we leave the 'msg' property
+ // intact.
+ // https://cloud.google.com/error-reporting/docs/formatting-error-messages
+ //
+ if (record.err && record.err.stack) {
+ record.message = record.err.stack;
+ record.serviceContext = this.serviceContext;
+ } else if (record.msg) {
+ // Simply rename `msg` to `message`.
+ record.message = record.msg;
+ delete record.msg;
+ }
+ }
+
+ const entryMetadata: types.StackdriverEntryMetadata = {
+ resource: this.resource,
+ timestamp: record.time,
+ severity: BUNYAN_TO_STACKDRIVER.get(Number(record.level)),
+ };
+
+ // If the record contains a httpRequest property, provide it on the entry
+ // metadata. This allows Stackdriver 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 a HttpRequest
+ // proto message, or the log entry would be rejected by the API. We do no
+ // validation here.
+ if (record.httpRequest) {
+ entryMetadata.httpRequest = record.httpRequest;
+ delete record.httpRequest;
+ }
+
+ // If the record contains a labels property, promote it to the entry
+ // metadata.
+ // https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
+ const proper = LoggingBunyan.properLabels(record.labels);
+ if (record.labels && proper) {
+ entryMetadata.labels = record.labels;
+ delete record.labels;
+ }
+
+ if (record[LOGGING_TRACE_KEY]) {
+ entryMetadata.trace = record[LOGGING_TRACE_KEY];
+ delete record[LOGGING_TRACE_KEY];
+ }
+
+ if (record[LOGGING_SPAN_KEY]) {
+ entryMetadata.spanId = record[LOGGING_SPAN_KEY];
+ delete record[LOGGING_SPAN_KEY];
+ }
+
+ if (LOGGING_SAMPLED_KEY in record) {
+ entryMetadata.traceSampled = record[LOGGING_SAMPLED_KEY];
+ delete record[LOGGING_SAMPLED_KEY];
+ }
+
+ return (
+ (
+ this.redirectToStdout
+ ? (this.cloudLog as LogSync)
+ : (this.cloudLog as Log)
+ )
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ .entry(entryMetadata as any, record)
+ );
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ static properLabels(labels: any) {
+ if (typeof labels !== 'object') return false;
+ for (const prop in labels) {
+ if (typeof labels[prop] !== 'string') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Intercept log entries as they are written so we can attempt to add the
+ * trace ID in the same continuation as the function that wrote the log,
+ * because the trace agent currently uses continuation local storage for the
+ * trace context.
+ *
+ * By the time the Writable stream buffer gets flushed and _write gets called
+ * we may well be in a different continuation.
+ */
+ write(record: types.BunyanLogRecord, callback?: Function): boolean;
+ write(
+ record: types.BunyanLogRecord,
+ encoding?: string,
+ callback?: Function
+ ): boolean;
+ // Writable.write used 'any' in function signature.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ write(...args: any[]): boolean {
+ let record = args[0];
+ let encoding: string | null = null;
+ type Callback = (error: Error | null | undefined) => void;
+ let callback: Callback | string;
+ if (typeof args[1] === 'string') {
+ encoding = args[1];
+ callback = args[2];
+ } else {
+ callback = args[1];
+ }
+ record = Object.assign({}, record);
+ if (!record[LOGGING_TRACE_KEY]) {
+ const trace = getCurrentTraceFromAgent();
+ if (trace) {
+ record[LOGGING_TRACE_KEY] = trace;
+ }
+ }
+ if (encoding !== null) {
+ return super.write.call(
+ this,
+ record,
+ encoding as BufferEncoding,
+ callback as Callback
+ );
+ } else {
+ return super.write.call(this, record, callback as BufferEncoding);
+ }
+ }
+
+ /**
+ * Relay a log entry to the logging agent. This is called by bunyan through
+ * Writable#write.
+ */
+ _write(record: types.BunyanLogRecord, encoding: string, callback: Function) {
+ const entry = this.formatEntry_(record);
+ this._writeCall(entry, callback);
+ }
+
+ /**
+ * Relay an array of log entries to the logging agent. This is called by
+ * bunyan through Writable#write.
+ */
+ // Writable._write used 'any' in function signature.
+ _writev(
+ chunks: Array<{
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ chunk: any;
+ encoding: string;
+ }>,
+ callback: Function
+ ) {
+ const entries = chunks.map(
+ (request: {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ chunk: any;
+ encoding: string;
+ }) => {
+ return this.formatEntry_(request.chunk);
+ }
+ );
+ this._writeCall(entries, callback);
+ }
+
+ /**
+ * Creates a combined callback which calls the this.defaultCallback and the Writable.write supplied callback
+ * @param callback The callback function provided by Writable
+ * @returns Combined callback which executes both, this.defaultCallback and one supplied by Writable.write
+ */
+ generateCallback(callback: Function): ApiResponseCallback {
+ // Make sure that both callbacks are called in case if provided
+ const newCallback: ApiResponseCallback = (
+ err: Error | null,
+ apiResponse?: {}
+ ) => {
+ if (callback) {
+ callback(err, apiResponse);
+ }
+ if (this.defaultCallback) {
+ this.defaultCallback(err, apiResponse);
+ }
+ };
+ return newCallback;
+ }
+
+ /**
+ * A helper function to make a write call
+ * @param entries The entries to be written
+ * @param callback The callback supplied by Writable.write
+ */
+ _writeCall(entries: Entry | Entry[], callback: Function) {
+ // First create instrumentation record if it is never written before
+ const alreadyWritten = setInstrumentationStatus(true);
+ if (!alreadyWritten) {
+ let instrumentationEntry = createDiagnosticEntry(
+ 'nodejs-bunyan',
+ this.getNodejsLibraryVersion()
+ );
+ // Update instrumentation record resource and logName
+ instrumentationEntry.metadata.resource = this.resource;
+ instrumentationEntry.metadata.severity = 'INFO';
+ instrumentationEntry = (
+ this.redirectToStdout
+ ? (this.cloudLog as LogSync)
+ : (this.cloudLog as Log)
+ ).entry(instrumentationEntry.metadata, instrumentationEntry.data);
+ entries = Array.isArray(entries) ? entries : [entries];
+ entries.push(instrumentationEntry);
+ }
+ if (this.redirectToStdout) {
+ (this.cloudLog as LogSync).write(entries);
+ // The LogSync class does not supports callback. However if callback provided and
+ // if this.defaultCallback exists, we should call it
+ this.generateCallback(callback)(null, undefined);
+ } else {
+ (this.cloudLog as Log).write(entries, this.generateCallback(callback));
+ }
+ }
+
+ /**
+ * Method used to retrieve the current logging-bunyan library version stored in NODEJS_BUNYAN_DEFAULT_LIBRARY_VERSION
+ * @returns The version of this library
+ */
+ getNodejsLibraryVersion() {
+ return NODEJS_BUNYAN_DEFAULT_LIBRARY_VERSION;
+ }
+}
+
+module.exports.BUNYAN_TO_STACKDRIVER = BUNYAN_TO_STACKDRIVER;
+
+/**
+ * Value: `logging.googleapis.com/trace`
+ *
+ * @name LoggingBunyan.LOGGING_TRACE_KEY
+ * @type {string}
+ */
+module.exports.LOGGING_TRACE_KEY = LOGGING_TRACE_KEY;
+
+/**
+ * Value: `logging.googleapis.com/spanId`
+ *
+ * @name LoggingBunyan.LOGGING_SPAN_KEY
+ * @type {string}
+ */
+module.exports.LOGGING_SPAN_KEY = LOGGING_SPAN_KEY;
+
+/**
+ * Value: `logging.googleapis.com/trace_sampled`
+ *
+ * @name LoggingBunyan.LOGGING_SAMPLED_KEY
+ * @type {string}
+ */
+module.exports.LOGGING_SAMPLED_KEY = LOGGING_SAMPLED_KEY;
diff --git a/handwritten/nodejs-logging-bunyan/src/middleware/express.ts b/handwritten/nodejs-logging-bunyan/src/middleware/express.ts
new file mode 100644
index 00000000000..caf5d31789d
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/src/middleware/express.ts
@@ -0,0 +1,127 @@
+/*!
+ * 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 {
+ HttpRequest,
+ middleware as commonMiddleware,
+} from '@google-cloud/logging';
+import * as bunyan from 'bunyan';
+import {GCPEnv} from 'google-auth-library';
+
+import {
+ LOGGING_TRACE_KEY,
+ LOGGING_SPAN_KEY,
+ LOGGING_SAMPLED_KEY,
+ LoggingBunyan,
+} from '../index';
+import * as types from '../types/core';
+
+export const APP_LOG_SUFFIX = 'applog';
+
+// @types/bunyan doesn't export Logger. Access it via ReturnType on
+// createLogger.
+export type Logger = ReturnType;
+
+export interface MiddlewareOptions extends types.Options {
+ level?: types.LogLevel;
+ // Enable the same behavior when running in Cloud Run as when running in Cloud Functions or App Engine to not create a request log entry
+ // that all the request-specific logs ("app logs") will nest under. GAE and GCF generate the parent request log automatically. Cloud Run
+ // also does this, so it is best to also skip this entry for Cloud Run. But that is considered a breaking change so this config option is
+ // introduced to enable this same behavior for Cloud Run. This might become the default behavior in a future major version.
+ skipParentEntryForCloudRun?: boolean;
+}
+
+export interface MiddlewareReturnType {
+ logger: Logger;
+ mw: ReturnType;
+}
+
+/**
+ * Express middleware
+ */
+export async function middleware(
+ options?: MiddlewareOptions
+): Promise {
+ const defaultOptions = {logName: 'bunyan_log', level: 'info'};
+ options = Object.assign({}, defaultOptions, options);
+
+ const loggingBunyanApp = new LoggingBunyan(
+ Object.assign({}, options, {
+ // For request bundling to work, the parent (request) and child (app) logs
+ // need to have distinct names. For exact requirements see:
+ // https://cloud.google.com/appengine/articles/logging#linking_app_logs_and_requests
+ logName: `${options.logName}_${APP_LOG_SUFFIX}`,
+ })
+ );
+ const logger = bunyan.createLogger({
+ name: `${options.logName}_${APP_LOG_SUFFIX}`,
+ streams: [loggingBunyanApp.stream(options.level as types.LogLevel)],
+ });
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const auth = (loggingBunyanApp.cloudLog as any).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 emitRequestLog;
+ if (
+ env !== GCPEnv.APP_ENGINE &&
+ env !== GCPEnv.CLOUD_FUNCTIONS &&
+ (env !== GCPEnv.CLOUD_RUN || !options.skipParentEntryForCloudRun)
+ ) {
+ const loggingBunyanReq = new LoggingBunyan(options);
+ const requestLogger = bunyan.createLogger({
+ name: options.logName!,
+ streams: [loggingBunyanReq.stream(options.level as types.LogLevel)],
+ });
+ emitRequestLog = (
+ httpRequest: HttpRequest,
+ trace: string,
+ span?: string,
+ sampled?: boolean
+ ) => {
+ requestLogger.info({
+ [LOGGING_TRACE_KEY]: trace,
+ [LOGGING_SPAN_KEY]: span,
+ [LOGGING_SAMPLED_KEY]: sampled,
+ httpRequest,
+ });
+ };
+ }
+
+ return {
+ logger,
+ mw: commonMiddleware.express.makeMiddleware(
+ projectId,
+ makeChildLogger,
+ emitRequestLog
+ ),
+ };
+
+ function makeChildLogger(trace: string, span?: string) {
+ return logger.child(
+ {[LOGGING_TRACE_KEY]: trace, [LOGGING_SPAN_KEY]: span},
+ true /* simple child */
+ );
+ }
+}
diff --git a/handwritten/nodejs-logging-bunyan/src/types/core.d.ts b/handwritten/nodejs-logging-bunyan/src/types/core.d.ts
new file mode 100644
index 00000000000..1b2dd2e36cf
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/src/types/core.d.ts
@@ -0,0 +1,323 @@
+/*!
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * 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 {ApiResponseCallback} from '@google-cloud/logging/build/src/log';
+
+export interface Options {
+ /**
+ * The name of the log that will receive messages written to this bunyan
+ * stream. Default: `bunyan_Log`.
+ */
+ logName?: string;
+ /**
+ * The monitored resource that the log stream 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;
+ /**
+ * 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.
+ */
+ projectId?: string;
+ /**
+ * 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.
+ */
+ keyFilename?: string;
+ /**
+ * Account email address. Required when using a .pem or .p12 keyFilename.
+ */
+ email?: string;
+ /**
+ * Credentials object.
+ */
+ credentials?: Credentials;
+ /**
+ * Automatically retry requests if the response is related to rate limits or
+ * certain intermittent server errors. We will exponentially backoff
+ * subsequent requests by default.
+ */
+ autoRetry?: boolean;
+ /**
+ * Maximum number of automatic retries attempted before returning the error.
+ */
+ maxRetries?: number;
+ /**
+ * Custom promise module to use instead of native Promises.
+ */
+ // TODO: address the correct type of promise.
+ promise?: {};
+ /**
+ * The host name of the service to send requests.
+ * Defaults to `logging.googleapis.com`.
+ */
+ apiEndpoint?: 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 list of JSON properties at the given full path to be truncated.
+ * Received values will be prepended to predefined list in the order received and duplicates discarded.
+ */
+ jsonFieldsToTruncate?: string[];
+ // A default global callback to be used for {@link LoggingBunyan} write calls
+ // when callback is not supplied by caller in function parameters
+ defaultCallback?: ApiResponseCallback;
+ /**
+ * 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;
+}
+
+export interface MonitoredResource {
+ type?: string;
+ labels?: {[key: string]: string};
+}
+
+export interface ServiceContext {
+ /**
+ * An identifier of the service, such as the name of the executable, job, or
+ * Google App Engine service name.
+ */
+ service?: string;
+ /**
+ * Represents the version of the service.
+ */
+ version?: string;
+}
+
+export interface StackdriverLog {
+ critical: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ debug: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ emergency: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ error: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ info: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ notice: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ warning: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ write: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ alert: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ entry: (metadata: {}, data: {} | string) => StackdriverEntry;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ logging: any;
+}
+
+export interface Credentials {
+ client_email?: string;
+ private_key?: string;
+}
+
+export interface StackdriverEntryMetadata {
+ resource?: MonitoredResource;
+ timestamp?: Date;
+ severity?: string; // figure out the correct type later
+ httpRequest?: HttpRequest;
+ labels?: {};
+ trace?: {};
+ spanId?: {};
+ traceSampled?: {};
+}
+
+export interface StackdriverLog {
+ critical: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ debug: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ emergency: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ error: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ info: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ notice: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ warning: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ write: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ alert: (
+ entry: StackdriverEntry | StackdriverEntry[],
+ options?: {},
+ callback?: (err: Error, apiResponse: {}) => void
+ ) => Promise;
+ entry: (metadata: {}, data: {} | string) => StackdriverEntry;
+}
+
+export interface StackdriverLogging {
+ Entry?: StackdriverEntry;
+ Log?: StackdriverLog;
+ Logging?: StackdriverLogging;
+ entry?: (
+ resource?: MonitoredResource,
+ data?: {message: string} | string
+ ) => StackdriverEntry;
+ // define additional properties and methods.
+}
+
+export interface StackdriverEntry {
+ constructor: (
+ metadata?: StackdriverEntryMetadata,
+ data?: {message: string} | string
+ ) => StackdriverEntry;
+ data?: StackdriverData | string;
+ metadata?: StackdriverEntryMetadata;
+}
+
+export interface StackdriverData {
+ serviceContext?: ServiceContext;
+ message?: string;
+ metadata?: Metadata;
+ pid?: string;
+ test?: {circular?: string};
+}
+
+export interface Metadata {
+ stack?: string;
+ httpRequest?: HttpRequest;
+}
+
+type LogWriteResponse = {}[];
+
+export interface HttpRequest {
+ requestMethod?: string;
+ requestUrl?: string;
+ requestSize?: number;
+ status?: number;
+ responseSize?: number;
+ userAgent?: string;
+ remoteIp?: string;
+ serverIp?: string;
+ referer?: string;
+ latency?: string | {seconds: number; nanos: number};
+ cacheLookup?: boolean;
+ cacheHit?: boolean;
+ cacheValidatedWithOriginServer?: boolean;
+ cacheFillBytes?: number;
+ protocol?: string;
+}
+
+export interface BunyanLogRecord {
+ message?: string;
+ msg?: string;
+ err?: Error;
+ serviceContext?: ServiceContext;
+ level?: string;
+ time?: Date;
+ httpRequest?: HttpRequest;
+ labels?: {};
+ // And arbitrary other properties.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ [key: string]: any;
+}
+
+export type LogLevel =
+ | 'trace'
+ | 'debug'
+ | 'info'
+ | 'warn'
+ | 'error'
+ | 'fatal'
+ | number;
+
+export interface StreamResponse {
+ level: LogLevel;
+ type: string;
+ stream: NodeJS.WritableStream;
+}
diff --git a/handwritten/nodejs-logging-bunyan/src/types/global.d.ts b/handwritten/nodejs-logging-bunyan/src/types/global.d.ts
new file mode 100644
index 00000000000..656a0be44f1
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/src/types/global.d.ts
@@ -0,0 +1,22 @@
+/*!
+ * Copyright 2018 Google LLC. All Rights Reserved.
+ *
+ * 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.
+ */
+
+export {};
+
+declare global {
+ // eslint-disable-next-line
+ var _google_trace_agent: any;
+}
diff --git a/handwritten/nodejs-logging-bunyan/system-test/errors-transport.ts b/handwritten/nodejs-logging-bunyan/system-test/errors-transport.ts
new file mode 100644
index 00000000000..6445c561792
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/system-test/errors-transport.ts
@@ -0,0 +1,134 @@
+// 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 * as common from '@google-cloud/common';
+import delay from 'delay';
+
+// 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';
+
+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) => {
+ return 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-bunyan/system-test/logging-bunyan.ts b/handwritten/nodejs-logging-bunyan/system-test/logging-bunyan.ts
new file mode 100644
index 00000000000..ec93e734352
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/system-test/logging-bunyan.ts
@@ -0,0 +1,301 @@
+/*!
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * 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 bunyan from 'bunyan';
+import * as uuid from 'uuid';
+import * as types from '../src/types/core';
+import {ErrorsApiTransport} from './errors-transport';
+import {Logging, LogSync} from '@google-cloud/logging';
+
+const logging = new Logging();
+import {LoggingBunyan} from '../src/index';
+import delay from 'delay';
+import * as instrumentation from '@google-cloud/logging/build/src/utils/instrumentation';
+
+const WRITE_CONSISTENCY_DELAY_MS = 90000;
+const MESSAGE = 'Diagnostic test';
+
+const UUID = uuid.v4();
+function logName(name: string) {
+ return `${UUID}_${name}`;
+}
+
+describe('LoggingBunyan', function () {
+ this.timeout(WRITE_CONSISTENCY_DELAY_MS);
+
+ const SERVICE = `logging-bunyan-system-test-${UUID}`;
+ const LOG_NAME = logName('logging-bunyan-system-test');
+ const loggingBunyan = new LoggingBunyan({
+ logName: LOG_NAME,
+ serviceContext: {service: SERVICE, version: 'none'},
+ });
+ const logger = bunyan.createLogger({
+ name: 'google-cloud-node-system-test',
+ streams: [loggingBunyan.stream('info')],
+ });
+
+ it('should create LoggingBunyan with LogSync', () => {
+ const loggingBunyan = new LoggingBunyan({
+ logName: LOG_NAME,
+ redirectToStdout: true,
+ });
+ assert.ok(loggingBunyan.cloudLog instanceof LogSync);
+ });
+
+ it('should create LoggingBunyan with LogSync and useMessageField is off', () => {
+ const loggingBunyan = new LoggingBunyan({
+ logName: LOG_NAME,
+ redirectToStdout: true,
+ useMessageField: false,
+ });
+ assert.ok(loggingBunyan.cloudLog instanceof LogSync);
+ assert.ok(loggingBunyan.cloudLog.useMessageField_ === false);
+ });
+
+ it('should write diagnostic entry', async () => {
+ instrumentation.setInstrumentationStatus(false);
+ const start = Date.now();
+ logger.info(MESSAGE);
+ const entries = await pollLogs(
+ LOG_NAME,
+ start,
+ 2,
+ WRITE_CONSISTENCY_DELAY_MS
+ );
+ assert.strictEqual(entries.length, 2);
+ let isDiagnosticPresent = false;
+ entries.forEach(entry => {
+ assert.ok(entry.data);
+ if (
+ Object.prototype.hasOwnProperty.call(
+ entry.data,
+ instrumentation.DIAGNOSTIC_INFO_KEY
+ )
+ ) {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const info = (entry.data as any)[instrumentation.DIAGNOSTIC_INFO_KEY][
+ instrumentation.INSTRUMENTATION_SOURCE_KEY
+ ];
+ assert.equal(info[0].name, 'nodejs-bunyan');
+ assert.ok(info[0].version.includes('.'));
+ assert.equal(info[1].name, 'nodejs');
+ assert.ok(info[1].version.includes('.'));
+ isDiagnosticPresent = true;
+ } else {
+ const data = entry.data as {message: string};
+ assert.ok(data.message.includes(MESSAGE));
+ }
+ });
+ assert.ok(isDiagnosticPresent);
+ });
+
+ it('should properly write log entries', async function () {
+ this.retries(3);
+ const timestamp = new Date();
+ const start = Date.now();
+
+ // Type of circular.circular cannot be determined..
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const circular: {circular?: any} = {};
+ circular.circular = circular;
+
+ const testData = [
+ {
+ args: ['first'],
+ level: 'info',
+ verify: (entry: types.StackdriverEntry) => {
+ assert.strictEqual(
+ (entry.data as types.StackdriverData).message,
+ 'first'
+ );
+ assert.strictEqual(
+ (entry.data as types.StackdriverData).pid,
+ process.pid
+ );
+ },
+ },
+
+ {
+ args: [new Error('second')],
+ level: 'error',
+ verify: (entry: types.StackdriverEntry) => {
+ assert(
+ ((entry.data as types.StackdriverData).message as string).includes(
+ 'Error: second'
+ )
+ );
+ assert.strictEqual(
+ (entry.data as types.StackdriverData).pid,
+ process.pid
+ );
+ },
+ },
+
+ {
+ args: [
+ {
+ test: circular,
+ },
+ 'third',
+ ],
+ level: 'info',
+ verify: (entry: types.StackdriverEntry) => {
+ assert.strictEqual(
+ (entry.data as types.StackdriverData).message,
+ 'third'
+ );
+ assert.strictEqual(
+ (entry.data as types.StackdriverData).pid,
+ process.pid
+ );
+ assert.deepStrictEqual((entry.data as types.StackdriverData).test, {
+ circular: '[Circular]',
+ });
+ },
+ },
+ ];
+
+ const earliest = {
+ args: [
+ {
+ time: timestamp,
+ },
+ 'earliest',
+ ],
+ level: 'info',
+ verify: (entry: types.StackdriverEntry) => {
+ assert.strictEqual(
+ (entry.data as types.StackdriverData).message,
+ 'earliest'
+ );
+ assert.strictEqual(
+ (entry.data as types.StackdriverData).pid,
+ process.pid
+ );
+ assert.strictEqual(
+ (
+ (entry.metadata as types.StackdriverEntryMetadata).timestamp as Date
+ ).toString(),
+ timestamp.toString()
+ );
+ },
+ };
+
+ // Forcibly insert a delay to cause 'third' to have a deterministically
+ // earlier timestamp.
+ await delay(10);
+
+ testData.forEach(test => {
+ // logger does not have index signature.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (logger as any)[test.level].apply(logger, test.args);
+ });
+ // `earliest` is sent last, but it should show up as the earliest entry.
+ // logger does not have index signature.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (logger as any)[earliest.level].apply(logger, earliest.args);
+ // insert into list as the earliest entry.
+ // TODO: identify the correct type for testData and earliest
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ testData.unshift(earliest as any);
+
+ const entries = await pollLogs(
+ LOG_NAME,
+ start,
+ testData.length,
+ WRITE_CONSISTENCY_DELAY_MS
+ );
+ assert.strictEqual(entries.length, testData.length);
+ entries.reverse().forEach((entry, index) => {
+ const test = testData[index];
+ test.verify(entry);
+ });
+ });
+
+ describe('ErrorReporting', () => {
+ const ERROR_REPORTING_POLL_TIMEOUT = WRITE_CONSISTENCY_DELAY_MS;
+ const errorsTransport = new ErrorsApiTransport();
+
+ it('reports errors when logging errors', async function () {
+ this.retries(3);
+ const start = Date.now();
+
+ const message = `an error at ${Date.now()}`;
+ logger.error(new Error(message));
+
+ const errors = await errorsTransport.pollForNewEvents(
+ SERVICE,
+ start,
+ ERROR_REPORTING_POLL_TIMEOUT
+ );
+
+ assert.strictEqual(
+ errors.length,
+ 1,
+ `expected 1 error but got ${require('util').inspect(errors)}`
+ );
+ const errEvent = errors[0];
+
+ assert.strictEqual(errEvent.serviceContext.service, SERVICE);
+ assert(errEvent.message.includes(`Error: ${message}`));
+ });
+ });
+});
+
+// polls for the entire array of entries to be greater than logTime.
+function pollLogs(
+ logName: string,
+ logTime: number,
+ size: number,
+ timeout: number
+) {
+ const p = new Promise((resolve, reject) => {
+ const end = Date.now() + timeout;
+ loop();
+
+ function loop() {
+ setTimeout(() => {
+ logging.log(logName).getEntries(
+ {
+ pageSize: size,
+ },
+ (err, entries) => {
+ if (!entries || entries.length < size) return loop();
+
+ const {receiveTimestamp} = (entries[entries.length - 1].metadata ||
+ {}) as {receiveTimestamp: {seconds: number; nanos: number}};
+ const timeMilliseconds =
+ receiveTimestamp.seconds * 1000 + receiveTimestamp.nanos * 1e-6;
+
+ if (timeMilliseconds >= logTime) {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ return resolve(entries as any);
+ }
+
+ if (Date.now() > end) {
+ return reject(new Error('timeout'));
+ }
+ loop();
+ }
+ );
+ }, 500);
+ }
+ });
+
+ return p;
+}
diff --git a/handwritten/nodejs-logging-bunyan/system-test/test-install.ts b/handwritten/nodejs-logging-bunyan/system-test/test-install.ts
new file mode 100644
index 00000000000..e4e0844f304
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/system-test/test-install.ts
@@ -0,0 +1,203 @@
+// 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 * as check from 'post-install-check';
+
+const TS_CODE_ARRAY: check.CodeSample[] = [
+ {
+ code: `import * as lb from '@google-cloud/logging-bunyan';
+import * as bunyan from 'bunyan';
+
+const loggingBunyan = new lb.LoggingBunyan();
+
+bunyan.createLogger({
+ name: 'my-service',
+ level: 'info',
+ streams: [
+ {stream: process.stdout},
+ loggingBunyan.stream('info'),
+ ],
+});`,
+ description: 'imports the module using * syntax',
+ dependencies: ['bunyan'],
+ devDependencies: ['@types/bunyan'],
+ },
+ {
+ code: `import {LoggingBunyan} from '@google-cloud/logging-bunyan';
+import * as bunyan from 'bunyan';
+
+const loggingBunyan = new LoggingBunyan();
+
+bunyan.createLogger({
+ name: 'my-service',
+ level: 'info',
+ streams: [
+ {stream: process.stdout},
+ loggingBunyan.stream('info'),
+ ],
+});`,
+ description: 'imports the module with {} syntax',
+ dependencies: ['bunyan'],
+ devDependencies: ['@types/bunyan'],
+ },
+ {
+ code: `import {LoggingBunyan} from '@google-cloud/logging-bunyan';
+import * as bunyan from 'bunyan';
+
+const loggingBunyan = new LoggingBunyan({
+ serviceContext: {
+ service: 'some service'
+ }
+});
+
+bunyan.createLogger({
+ name: 'my-service',
+ level: 'info',
+ streams: [
+ {stream: process.stdout},
+ loggingBunyan.stream('info'),
+ ],
+});`,
+ description:
+ 'imports the module and starts with a partial `serviceContext`',
+ dependencies: ['bunyan'],
+ devDependencies: ['@types/bunyan'],
+ },
+ {
+ code: `import {LoggingBunyan} from '@google-cloud/logging-bunyan';
+import * as bunyan from 'bunyan';
+
+const loggingBunyan = new LoggingBunyan({
+ projectId: 'some-project',
+ serviceContext: {
+ service: 'Some service',
+ version: 'Some version'
+ }
+});
+
+bunyan.createLogger({
+ name: 'my-service',
+ level: 'info',
+ streams: [
+ {stream: process.stdout},
+ loggingBunyan.stream('info'),
+ ],
+});`,
+ description:
+ 'imports the module and starts with a complete `serviceContext`',
+ dependencies: ['bunyan'],
+ devDependencies: ['@types/bunyan'],
+ },
+ {
+ code: `import * as lb from '@google-cloud/logging-bunyan';
+import * as express from 'express';
+
+async function main() {
+ const {logger, mw} = await lb.express.middleware();
+ const app = express();
+ app.use(mw);
+}`,
+ description: 'can be used with express',
+ dependencies: ['express', 'bunyan'],
+ devDependencies: ['@types/bunyan', '@types/express'],
+ },
+];
+
+const JS_CODE_ARRAY: check.CodeSample[] = [
+ {
+ code: `const LoggingBunyan = require('@google-cloud/logging-bunyan').LoggingBunyan;
+const bunyan = require('bunyan');
+
+const loggingBunyan = new LoggingBunyan();
+
+bunyan.createLogger({
+ name: 'my-service',
+ level: 'info',
+ streams: [
+ {stream: process.stdout},
+ loggingBunyan.stream('info'),
+ ],
+});`,
+ description: 'requires the module using Node 4+ syntax',
+ dependencies: ['bunyan'],
+ devDependencies: [],
+ },
+ {
+ code: `const LoggingBunyan = require('@google-cloud/logging-bunyan').LoggingBunyan;
+const bunyan = require('bunyan');
+
+const loggingBunyan = new LoggingBunyan({
+ serviceContext: {
+ service: 'some service'
+ }
+});
+
+bunyan.createLogger({
+ name: 'my-service',
+ level: 'info',
+ streams: [
+ {stream: process.stdout},
+ loggingBunyan.stream('info'),
+ ],
+});`,
+ description:
+ 'requires the module and starts with a partial `serviceContext`',
+ dependencies: ['bunyan'],
+ devDependencies: [],
+ },
+ {
+ code: `const LoggingBunyan = require('@google-cloud/logging-bunyan').LoggingBunyan;
+const bunyan = require('bunyan');
+
+const loggingBunyan = new LoggingBunyan({
+ projectId: 'some-project',
+ serviceContext: {
+ service: 'Some service',
+ version: 'Some version'
+ }
+});
+
+bunyan.createLogger({
+ name: 'my-service',
+ level: 'info',
+ streams: [
+ {stream: process.stdout},
+ loggingBunyan.stream('info'),
+ ],
+});`,
+ description:
+ 'requires the module and starts with a complete `serviceContext`',
+ dependencies: ['bunyan'],
+ devDependencies: [],
+ },
+ {
+ code: `const lb = require('@google-cloud/logging-bunyan');
+const express = require('express');
+
+async function main() {
+ lb.express.middleware().then(result => {
+ const app = express();
+ app.use(result.mw);
+ }).catch((err) => {
+ console.error(err);
+ process.exit(1);
+ });
+}`,
+ description: 'can be used with express',
+ dependencies: ['express', 'bunyan'],
+ devDependencies: [],
+ },
+];
+
+check.testInstallation(TS_CODE_ARRAY, JS_CODE_ARRAY, {timeout: 5 * 60 * 1000});
diff --git a/handwritten/nodejs-logging-bunyan/system-test/test-middleware-express.ts b/handwritten/nodejs-logging-bunyan/system-test/test-middleware-express.ts
new file mode 100644
index 00000000000..4e76584563e
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/system-test/test-middleware-express.ts
@@ -0,0 +1,86 @@
+/*!
+ * 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.
+ */
+
+/* AN EXAMPLE RATHER THAN A TEST AT THIS POINT */
+
+import * as assert from 'assert';
+import {describe, it, before} from 'mocha';
+import delay from 'delay';
+import * as uuid from 'uuid';
+import {express as elb} from '../src/index';
+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 = `bunyan-system-test-${uuid.v4()}`;
+
+describe('express middleware', () => {
+ let logger: elb.MiddlewareReturnType['logger'];
+ let mw: elb.MiddlewareReturnType['mw'];
+
+ before(async () => {
+ ({logger, mw} = await elb.middleware({logName: LOG_NAME, level: 'info'}));
+ });
+
+ describe('global logger', () => {
+ it('should properly write log entries', async function () {
+ this.timeout(TEST_TIMEOUT);
+ const LOG_MESSAGE = `unique log message ${uuid.v4()}`;
+ logger.info(LOG_MESSAGE);
+
+ await delay(WRITE_CONSISTENCY_DELAY_MS);
+
+ const log = logging.log(`${LOG_NAME}_applog`);
+ 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 (done) {
+ this.timeout(TEST_TIMEOUT);
+ const LOG_MESSAGE = `correlated log message ${uuid.v4()}`;
+ const fakeRequest = {headers: {fake: 'header'}};
+ const fakeResponse = {};
+ 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 log = logging.log(`${LOG_NAME}_applog`);
+ const entries = (await log.getEntries({pageSize: 1}))[0];
+ assert.strictEqual(entries.length, 1);
+ const entry = entries[0];
+ assert.strictEqual(LOG_MESSAGE, entry.data.message);
+ assert(entry.metadata.trace, 'should have a trace property');
+ assert(entry.metadata.trace!.match(/projects\/.*\/traces\/.*/));
+ assert(entry.metadata.spanId, 'should have a span property');
+ assert(entry.metadata.spanId!.match(/^[0-9]*/));
+ assert.strictEqual(entry.metadata.traceSampled, false);
+ done();
+ };
+
+ // 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-bunyan/test/index.ts b/handwritten/nodejs-logging-bunyan/test/index.ts
new file mode 100644
index 00000000000..6ebd30b08d0
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/test/index.ts
@@ -0,0 +1,744 @@
+// Copyright 2017 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, afterEach} from 'mocha';
+import * as proxyquire from 'proxyquire';
+import {inspect} from 'util';
+
+import {LoggingBunyan} from '../src';
+import * as types from '../src/types/core';
+
+interface Options {
+ logName?: string;
+ resource: {};
+ serviceContext: {
+ service: string;
+ };
+ apiEndpoint: string;
+ jsonFieldsToTruncate: string[];
+}
+interface FakeLogType {
+ entry?: () => void;
+ write?: () => void;
+ logging: {auth: {getEnv: Function}};
+}
+
+describe('logging-bunyan', () => {
+ const FAKE_LOG_INSTANCE: FakeLogType = {
+ logging: {
+ auth: {
+ getEnv: () => {
+ return 'foo';
+ },
+ },
+ },
+ };
+ let fakeLogInstance: FakeLogType;
+ let fakeLoggingOptions_: types.Options | null;
+ let fakeLogName_: string | null;
+ let fakeLogOptions_: types.Options;
+ let fakeWritableOptions_: types.Options;
+ let fakeDetectedServiceContext: types.ServiceContext | null;
+
+ function fakeLogging(options: types.Options) {
+ fakeLoggingOptions_ = options;
+ return {
+ log(logName: string, options: types.Options) {
+ fakeLogName_ = logName;
+ fakeLogOptions_ = options;
+ return fakeLogInstance;
+ },
+ };
+ }
+
+ function FakeWritable(options: types.Options) {
+ fakeWritableOptions_ = options;
+ }
+
+ // Writable.write used 'any' in function signature.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ FakeWritable.prototype.write = (
+ chunk: {},
+ encoding: string,
+ callback: Function
+ ) => {
+ // Function cannot pass as type in setImmediate.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ setImmediate(callback as any);
+ };
+
+ const fakeStream = {
+ Writable: FakeWritable,
+ };
+
+ const fakeDetectServiceContext = () => {
+ if (fakeDetectedServiceContext === null) {
+ return Promise.reject(new Error('fake error'));
+ }
+ return Promise.resolve(fakeDetectedServiceContext);
+ };
+ const loggingBunyanLib = proxyquire('../src/index.js', {
+ '@google-cloud/logging': {
+ Logging: fakeLogging,
+ detectServiceContext: fakeDetectServiceContext,
+ },
+ stream: fakeStream,
+ });
+ const loggingBunyanCached = proxyquire('../src/index.js', {
+ '@google-cloud/logging': {
+ Logging: fakeLogging,
+ detectServiceContext: fakeDetectServiceContext,
+ },
+ stream: fakeStream,
+ });
+
+ // loggingBunyan is loggingBunyan namespace which cannot be determined type.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ let loggingBunyan: any;
+
+ const TRUNCATE_FIELD =
+ 'jsonPayload.fields.metadata.structValue.fields.custom.stringValue';
+
+ const OPTIONS = {
+ logName: 'log-name',
+ resource: {},
+ serviceContext: {
+ service: 'fake-service',
+ },
+ apiEndpoint: 'fake.local',
+ jsonFieldsToTruncate: [TRUNCATE_FIELD],
+ };
+
+ const RECORD = {
+ level: 30,
+ time: '2012-06-19T21:34:19.906Z',
+ };
+
+ beforeEach(() => {
+ fakeLogInstance = {...FAKE_LOG_INSTANCE};
+ fakeLoggingOptions_ = null;
+ fakeLogName_ = null;
+ fakeDetectedServiceContext = null;
+
+ Object.assign(true, loggingBunyanLib.LoggingBunyan, loggingBunyanCached);
+ loggingBunyan = new loggingBunyanLib.LoggingBunyan(OPTIONS);
+ });
+
+ describe('instantiation', () => {
+ it('should be an object mode Writable', () => {
+ assert(loggingBunyan instanceof FakeWritable);
+ assert.deepStrictEqual(fakeWritableOptions_, {objectMode: true});
+ });
+
+ it('should localize the provided resource', () => {
+ assert.strictEqual(loggingBunyan.resource, OPTIONS.resource);
+ });
+
+ it('should localize the provided service context', () => {
+ assert.strictEqual(loggingBunyan.serviceContext, OPTIONS.serviceContext);
+ });
+
+ it('should localize Log instance using provided name', () => {
+ assert.strictEqual(fakeLoggingOptions_, OPTIONS);
+ assert.strictEqual(fakeLogName_, OPTIONS.logName);
+ });
+
+ it('should localize Log instance using provided jsonFieldsToTruncate in options', () => {
+ assert.strictEqual(fakeLoggingOptions_, OPTIONS);
+ assert.strictEqual(
+ fakeLogOptions_.jsonFieldsToTruncate,
+ OPTIONS.jsonFieldsToTruncate
+ );
+ });
+
+ it('should localize Log instance using default name, removeCircular and maxEntrySize options', () => {
+ const optionsWithoutLogName: Options = Object.assign({}, OPTIONS);
+ delete optionsWithoutLogName.logName;
+ new loggingBunyanLib.LoggingBunyan(optionsWithoutLogName);
+ assert.strictEqual(fakeLoggingOptions_, optionsWithoutLogName);
+ assert.strictEqual(fakeLogName_, 'bunyan_log');
+ assert.deepStrictEqual(fakeLogOptions_, {
+ removeCircular: true,
+ maxEntrySize: 250000,
+ jsonFieldsToTruncate: [TRUNCATE_FIELD],
+ });
+ });
+
+ it('should not throw if a serviceContext is not specified', () => {
+ // tslint:disable-next-line:no-unused-expression
+ new loggingBunyanLib.LoggingBunyan();
+ });
+
+ it('should throw if a serviceContext is specified without a service', done => {
+ try {
+ // tslint:disable-next-line:no-unused-expression
+ new loggingBunyanLib.LoggingBunyan({serviceContext: {}});
+ } catch (err) {
+ assert.strictEqual(
+ (err as Error).message,
+ "If 'serviceContext' is specified then " +
+ "'serviceContext.service' is required."
+ );
+ done();
+ }
+ });
+
+ it('should not attempt to discover service context if passed', () => {
+ const serviceContext = {service: 'foo'};
+ // tslint:disable-next-line:no-unused-expression
+ new loggingBunyanLib.LoggingBunyan({serviceContext});
+ });
+
+ it('should attempt to discover service context if not passed', done => {
+ const serviceContext = {service: 'foo'};
+ fakeDetectedServiceContext = serviceContext;
+ const lb = new loggingBunyanLib.LoggingBunyan();
+ assert.strictEqual(lb.serviceContext, undefined);
+ setTimeout(() => {
+ assert.deepStrictEqual(lb.serviceContext, serviceContext);
+ done();
+ }, 10);
+ });
+
+ it('should handle errors in discovering service context', done => {
+ fakeDetectedServiceContext = null;
+ const lb = new loggingBunyanLib.LoggingBunyan();
+ assert.strictEqual(lb.serviceContext, undefined);
+ setTimeout(() => {
+ assert.deepStrictEqual(lb.serviceContext, undefined);
+ done();
+ }, 10);
+ });
+ });
+
+ describe('stream', () => {
+ it('should return a properly formatted object', () => {
+ const level = 'info';
+ const stream = loggingBunyan.stream(level);
+
+ assert.strictEqual(stream.level, level);
+ assert.strictEqual(stream.type, 'raw');
+ assert.strictEqual(stream.stream, loggingBunyan);
+ });
+ });
+
+ describe('properLabels', () => {
+ it('should validate labels correctly', () => {
+ const properLabels = [
+ {},
+ [],
+ {key: 'value'},
+ ['a', 'b'],
+ {a: 'b', c: 'd'},
+ {
+ key: 'value',
+ [Symbol('symbolKey')]: 'value2',
+ }, // symbol gets ignored.
+ ];
+ const improperLabels = [
+ true,
+ false,
+ undefined,
+ -1,
+ NaN,
+ () => {},
+ 'a string',
+ Symbol('a symbol'),
+ {key: {nested: 'object'}},
+ {key: -1},
+ {key: false},
+ {key: Symbol('another symbol')},
+ ];
+
+ for (const labels of properLabels) {
+ assert.strictEqual(
+ true,
+ LoggingBunyan.properLabels(labels),
+ `expected ${inspect(labels)} to be proper`
+ );
+ }
+ for (const labels of improperLabels) {
+ assert.strictEqual(
+ false,
+ LoggingBunyan.properLabels(labels),
+ `expected ${inspect(labels)} to be improper`
+ );
+ }
+ });
+ });
+
+ describe('formatEntry_', () => {
+ it('should throw an error if record is a string', () => {
+ assert.throws(() => {
+ loggingBunyan.formatEntry_('string record');
+ }, new RegExp('@google-cloud/logging-bunyan only works as a raw bunyan stream type.'));
+ });
+
+ it('should properly create an entry', done => {
+ loggingBunyan.cloudLog.entry = (
+ entryMetadata: types.StackdriverEntryMetadata,
+ record: types.StackdriverEntryMetadata
+ ) => {
+ assert.deepStrictEqual(entryMetadata, {
+ resource: loggingBunyan.resource,
+ timestamp: RECORD.time,
+ severity: 'INFO',
+ });
+ assert.deepStrictEqual(record, RECORD);
+ done();
+ };
+
+ loggingBunyan.formatEntry_(RECORD);
+ });
+
+ it('should rename the msg property to message', done => {
+ const recordWithMsg = Object.assign({msg: 'msg'}, RECORD);
+ const recordWithMessage = Object.assign({message: 'msg'}, RECORD);
+
+ loggingBunyan.cloudLog.entry = (
+ entryMetadata: types.StackdriverEntryMetadata,
+ record: types.StackdriverEntryMetadata
+ ) => {
+ assert.deepStrictEqual(record, recordWithMessage);
+ done();
+ };
+
+ loggingBunyan.formatEntry_(recordWithMsg);
+ });
+
+ it('should inject the error stack as the message', done => {
+ const record = Object.assign(
+ {
+ msg: 'msg',
+ err: {
+ stack: 'the stack',
+ },
+ },
+ RECORD
+ );
+ const expectedRecord = Object.assign(
+ {
+ msg: 'msg',
+ err: {
+ stack: 'the stack',
+ },
+ message: 'the stack',
+ serviceContext: OPTIONS.serviceContext,
+ },
+ RECORD
+ );
+
+ loggingBunyan.cloudLog.entry = (
+ entryMetadata: types.StackdriverEntryMetadata,
+ record_: types.StackdriverEntryMetadata
+ ) => {
+ assert.deepStrictEqual(record_, expectedRecord);
+ done();
+ };
+
+ loggingBunyan.formatEntry_(record);
+ });
+
+ it('should leave message property intact when present', done => {
+ const record = Object.assign(
+ {
+ msg: 'msg',
+ message: 'message',
+ err: {
+ stack: 'the stack',
+ },
+ },
+ RECORD
+ );
+
+ loggingBunyan.cloudLog.entry = (
+ entryMetadata: types.StackdriverEntryMetadata,
+ record_: types.StackdriverEntryMetadata
+ ) => {
+ assert.deepStrictEqual(record_, record);
+ done();
+ };
+
+ loggingBunyan.formatEntry_(record);
+ });
+
+ it('should promote the httpRequest property to metadata', done => {
+ const HTTP_REQUEST = {
+ statusCode: 418,
+ };
+ const recordWithRequest = Object.assign(
+ {
+ httpRequest: HTTP_REQUEST,
+ },
+ RECORD
+ );
+
+ loggingBunyan.cloudLog.entry = (
+ entryMetadata: types.StackdriverEntryMetadata,
+ record: string | types.BunyanLogRecord
+ ) => {
+ assert.deepStrictEqual(entryMetadata, {
+ resource: loggingBunyan.resource,
+ timestamp: RECORD.time,
+ severity: 'INFO',
+ httpRequest: HTTP_REQUEST,
+ });
+ assert.deepStrictEqual(record, RECORD);
+ done();
+ };
+
+ loggingBunyan.formatEntry_(recordWithRequest);
+ });
+
+ it('should promote properly formatted labels to metadata', done => {
+ const labels = {key: 'value', 0: 'value2'};
+ const recordWithLabels = {...RECORD, labels};
+ loggingBunyan.cloudLog.entry = (
+ entryMetadata: types.StackdriverEntryMetadata,
+ record: string | types.BunyanLogRecord
+ ) => {
+ assert.deepStrictEqual(entryMetadata.labels, labels);
+ assert.deepStrictEqual(record, RECORD);
+ done();
+ };
+ loggingBunyan.formatEntry_(recordWithLabels);
+ });
+
+ it('should not promote ill-formatted labels to metadata', done => {
+ const labels = {key: -1}; // values must be strings.
+ const recordWithLabels = {...RECORD, labels};
+ loggingBunyan.cloudLog.entry = (
+ entryMetadata: types.StackdriverEntryMetadata,
+ record: string | types.BunyanLogRecord
+ ) => {
+ assert(entryMetadata.labels === undefined);
+ assert.deepStrictEqual(record, recordWithLabels);
+ done();
+ };
+ loggingBunyan.formatEntry_(recordWithLabels);
+ });
+
+ it('should promote prefixed trace properties to metadata', done => {
+ const recordWithTrace = Object.assign({}, RECORD);
+ // recordWithTrace does not have index signature.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (recordWithTrace as any)[loggingBunyanLib.LOGGING_TRACE_KEY] = 'trace1';
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (recordWithTrace as any)[loggingBunyanLib.LOGGING_SPAN_KEY] = 'span1';
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (recordWithTrace as any)[loggingBunyanLib.LOGGING_SAMPLED_KEY] = true;
+
+ loggingBunyan.cloudLog.entry = (
+ entryMetadata: types.StackdriverEntryMetadata,
+ record: string | types.BunyanLogRecord
+ ) => {
+ assert.deepStrictEqual(entryMetadata, {
+ resource: loggingBunyan.resource,
+ timestamp: RECORD.time,
+ severity: 'INFO',
+ trace: 'trace1',
+ spanId: 'span1',
+ traceSampled: true,
+ });
+ assert.deepStrictEqual(record, RECORD);
+ done();
+ };
+
+ loggingBunyan.formatEntry_(recordWithTrace);
+ });
+
+ it('should promote a `false` traceSampled property to metadata', done => {
+ const recordWithTrace = Object.assign({}, RECORD);
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (recordWithTrace as any)[loggingBunyanLib.LOGGING_SAMPLED_KEY] = false;
+
+ loggingBunyan.cloudLog.entry = (
+ entryMetadata: types.StackdriverEntryMetadata,
+ record: string | types.BunyanLogRecord
+ ) => {
+ assert.deepStrictEqual(entryMetadata, {
+ resource: loggingBunyan.resource,
+ timestamp: RECORD.time,
+ severity: 'INFO',
+ traceSampled: false,
+ });
+ assert.deepStrictEqual(record, RECORD);
+ done();
+ };
+
+ loggingBunyan.formatEntry_(recordWithTrace);
+ });
+ });
+
+ describe('write', () => {
+ const oldWritableWrite = FakeWritable.prototype.write;
+ const oldTraceAgent = global._google_trace_agent;
+
+ afterEach(() => {
+ FakeWritable.prototype.write = oldWritableWrite;
+ global._google_trace_agent = oldTraceAgent;
+ });
+
+ it('should not set trace property if trace unavailable', done => {
+ global._google_trace_agent = undefined;
+
+ FakeWritable.prototype.write = function (
+ // Writable.write used 'any' in function signature.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ record: any,
+ encoding: string,
+ callback: Function
+ ) {
+ assert.deepStrictEqual(record, RECORD);
+ assert.strictEqual(encoding, 'encoding');
+ assert.strictEqual(callback, assert.ifError);
+ assert.strictEqual(this, loggingBunyan);
+ done();
+ };
+
+ loggingBunyan.write(RECORD, 'encoding', assert.ifError);
+ });
+
+ it('should set prefixed trace property if trace available', done => {
+ global._google_trace_agent = {
+ getCurrentContextId: () => {
+ return 'trace1';
+ },
+ getWriterProjectId: () => {
+ return 'project1';
+ },
+ };
+ const recordWithoutTrace = Object.assign({}, RECORD);
+ const recordWithTrace = Object.assign({}, RECORD);
+ // recordWithTrace does not have index signature.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (recordWithTrace as any)[loggingBunyanLib.LOGGING_TRACE_KEY] =
+ 'projects/project1/traces/trace1';
+
+ FakeWritable.prototype.write = function (
+ // Writable.write used 'any' in function signature.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ record: any,
+ encoding: string,
+ callback: Function
+ ) {
+ // Check that trace field added to record before calling Writable.write
+ assert.deepStrictEqual(record, recordWithTrace);
+
+ // Check that the original record passed in was not mutated
+ assert.deepStrictEqual(recordWithoutTrace, RECORD);
+
+ assert.strictEqual(encoding, 'encoding');
+ assert.strictEqual(callback, assert.ifError);
+ assert.strictEqual(this, loggingBunyan);
+ done();
+ };
+
+ loggingBunyan.write(recordWithoutTrace, 'encoding', assert.ifError);
+ });
+
+ it('should leave prefixed trace property as is if set', done => {
+ const oldTraceAgent = global._google_trace_agent;
+ global._google_trace_agent = {
+ getCurrentContextId: () => {
+ return 'trace-from-agent';
+ },
+ getWriterProjectId: () => {
+ return 'project1';
+ },
+ };
+ const recordWithTraceAlreadySet = Object.assign({}, RECORD);
+ // recordWithTraceAlreadySet does not have index signature.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (recordWithTraceAlreadySet as any)[loggingBunyanLib.LOGGING_TRACE_KEY] =
+ 'trace1';
+
+ FakeWritable.prototype.write = function (
+ // Writable.write used 'any' in function signature.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ record: any,
+ encoding: string,
+ callback: Function
+ ) {
+ assert.deepStrictEqual(record, recordWithTraceAlreadySet);
+ assert.strictEqual(encoding, '');
+ assert.strictEqual(callback, assert.ifError);
+ assert.strictEqual(this, loggingBunyan);
+ done();
+ };
+
+ loggingBunyan.write(recordWithTraceAlreadySet, '', assert.ifError);
+
+ global._google_trace_agent = oldTraceAgent;
+ });
+ });
+
+ it('should not set prefixed trace property if trace unavailable', () => {
+ FakeWritable.prototype.write = function (
+ // Writable.write used 'any' in function signature.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ record: any,
+ encoding: string,
+ callback: Function
+ ) {
+ assert.deepStrictEqual(record, RECORD);
+ assert.strictEqual(encoding, '');
+ assert.strictEqual(callback, assert.ifError);
+ assert.strictEqual(this, loggingBunyan);
+ };
+ const oldTraceAgent = global._google_trace_agent;
+
+ global._google_trace_agent = {};
+ loggingBunyan.write(RECORD, '', assert.ifError);
+
+ global._google_trace_agent = {
+ getCurrentContextId: () => {
+ return null;
+ },
+ getWriterProjectId: () => {
+ return null;
+ },
+ };
+ loggingBunyan.write(RECORD, '', assert.ifError);
+ global._google_trace_agent = {
+ getCurrentContextId: () => {
+ return null;
+ },
+ getWriterProjectId: () => {
+ return 'project1';
+ },
+ };
+ loggingBunyan.write(RECORD, '', assert.ifError);
+
+ global._google_trace_agent = {
+ getCurrentContextId: () => {
+ return 'trace1';
+ },
+ getWriterProjectId: () => {
+ return null;
+ },
+ };
+ loggingBunyan.write(RECORD, '', assert.ifError);
+
+ global._google_trace_agent = oldTraceAgent;
+ });
+
+ describe('_write', () => {
+ beforeEach(() => {
+ fakeLogInstance.entry = () => {};
+ fakeLogInstance.write = () => {};
+ });
+
+ it('should format the record', done => {
+ loggingBunyan.formatEntry_ = (record: string | types.BunyanLogRecord) => {
+ assert.strictEqual(record, RECORD);
+ done();
+ };
+
+ loggingBunyan._write(RECORD, '', assert.ifError);
+ });
+
+ it('should write the record to the log instance', done => {
+ const entry = {};
+
+ loggingBunyan.cloudLog.entry = () => {
+ return entry;
+ };
+
+ loggingBunyan.cloudLog.write =
+ // Writable.write used 'any' in function signature.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (entries: any, callback: Function) => {
+ assert.strictEqual(entries, entry);
+ callback(); // done()
+ };
+
+ loggingBunyan._write(RECORD, '', done);
+ });
+
+ it('should write the record and call default callback', done => {
+ let isCallbackCalled = false;
+ loggingBunyan.cloudLog.entry = () => {
+ return {};
+ };
+ loggingBunyan.defaultCallback = () => {
+ isCallbackCalled = true;
+ };
+ loggingBunyan.cloudLog.write =
+ // Writable.write used 'any' in function signature.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (entries: any, callback: Function) => {
+ callback();
+ };
+ loggingBunyan._write(RECORD, '', () => {});
+ assert.strictEqual(isCallbackCalled, true);
+ done();
+ });
+ });
+
+ describe('_writev', () => {
+ const RECORDS = [{chunk: RECORD}, {chunk: RECORD}];
+ beforeEach(() => {
+ fakeLogInstance.entry = () => {};
+ fakeLogInstance.write = () => {};
+ });
+
+ it('should format the records', done => {
+ let numFormatted = 0;
+ loggingBunyan.formatEntry_ = (record: string | types.BunyanLogRecord) => {
+ assert.strictEqual(record, RECORD);
+ if (++numFormatted === RECORDS.length) {
+ done();
+ }
+ };
+
+ loggingBunyan._writev(RECORDS, assert.ifError);
+ });
+
+ it('should write the records to the log instance', done => {
+ const entry = {};
+
+ loggingBunyan.cloudLog.entry = () => {
+ return entry;
+ };
+
+ loggingBunyan.cloudLog.write =
+ // Writable.write used 'any' in function signature.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (entries: any, callback: Function) => {
+ assert.deepStrictEqual(entries, [entry, entry]);
+ callback(); // done()
+ };
+
+ loggingBunyan._writev(RECORDS, done);
+ });
+ });
+
+ describe('BUNYAN_TO_STACKDRIVER', () => {
+ it('should correctly map to Stackdriver Logging levels', () => {
+ const bunyanToStackdriver: Map = new Map([
+ [60, 'CRITICAL'],
+ [50, 'ERROR'],
+ [40, 'WARNING'],
+ [30, 'INFO'],
+ [20, 'DEBUG'],
+ [10, 'DEBUG'],
+ ]);
+ assert.deepStrictEqual(
+ loggingBunyanLib.BUNYAN_TO_STACKDRIVER,
+ bunyanToStackdriver
+ );
+ });
+ });
+});
diff --git a/handwritten/nodejs-logging-bunyan/test/middleware/test-express.ts b/handwritten/nodejs-logging-bunyan/test/middleware/test-express.ts
new file mode 100644
index 00000000000..1469f0deabd
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/test/middleware/test-express.ts
@@ -0,0 +1,143 @@
+/*!
+ * 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 * as assert from 'assert';
+import {describe, it, beforeEach} from 'mocha';
+import {GCPEnv} from 'google-auth-library';
+import * as proxyquire from 'proxyquire';
+
+// types-only import. Actual require is done through proxyquire below.
+import {MiddlewareOptions} from '../../src/middleware/express';
+
+const FAKE_PROJECT_ID = 'project-🦄';
+const FAKE_GENERATED_MIDDLEWARE = () => {};
+
+const FAKE_ENVIRONMENT = 'FAKE_ENVIRONMENT';
+
+let authEnvironment: string;
+let passedOptions: Array;
+
+class FakeLoggingBunyan {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ cloudLog: any;
+ constructor(options: MiddlewareOptions) {
+ passedOptions.push(options);
+ this.cloudLog = {
+ logging: {
+ auth: {
+ async getProjectId() {
+ return FAKE_PROJECT_ID;
+ },
+ async getEnv() {
+ return authEnvironment;
+ },
+ },
+ },
+ };
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ stream(level: any) {
+ return {level, type: 'raw', stream: this};
+ }
+}
+
+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 {middleware, APP_LOG_SUFFIX} = proxyquire(
+ '../../src/middleware/express',
+ {
+ '../../src/index': {LoggingBunyan: FakeLoggingBunyan},
+ '@google-cloud/logging': {
+ middleware: {express: {makeMiddleware: fakeMakeMiddleware}},
+ },
+ }
+);
+
+describe('middleware/express', () => {
+ beforeEach(() => {
+ passedOptions = [];
+ passedProjectId = undefined;
+ passedEmitRequestLog = undefined;
+ authEnvironment = FAKE_ENVIRONMENT;
+ });
+
+ it('should create and return a middleware', async () => {
+ const {mw} = await middleware();
+ assert.strictEqual(mw, FAKE_GENERATED_MIDDLEWARE);
+ });
+
+ it('should generate two loggers with default logName and level', async () => {
+ await middleware();
+ // Should generate two loggers with the expected names.
+ assert.ok(passedOptions);
+ assert.strictEqual(passedOptions.length, 2);
+ assert.ok(
+ passedOptions.some(
+ option => option!.logName === `bunyan_log_${APP_LOG_SUFFIX}`
+ )
+ );
+ assert.ok(passedOptions.some(option => option!.logName === 'bunyan_log'));
+ assert.ok(passedOptions.every(option => option!.level === 'info'));
+ });
+
+ it('should prefer user-provided logName and level', async () => {
+ const LOGNAME = '㏒';
+ const LEVEL = 'fatal';
+ const OPTIONS = {logName: LOGNAME, level: LEVEL};
+ await middleware(OPTIONS);
+ assert.ok(passedOptions);
+ assert.strictEqual(passedOptions.length, 2);
+ assert.ok(
+ passedOptions.some(
+ option => option!.logName === `${LOGNAME}_${APP_LOG_SUFFIX}`
+ )
+ );
+ assert.ok(passedOptions.some(option => option!.logName === LOGNAME));
+ assert.ok(passedOptions.every(option => option!.level === LEVEL));
+ });
+
+ it('should acquire the projectId and pass to makeMiddleware', async () => {
+ await middleware();
+ 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;
+ if (env === GCPEnv.CLOUD_RUN) {
+ // Cloud Run needs explicit option flag to enable this behavior until we can make breaking change in next major version
+ await middleware({skipParentEntryForCloudRun: true});
+ } else {
+ await middleware();
+ }
+ 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-bunyan/tsconfig.json b/handwritten/nodejs-logging-bunyan/tsconfig.json
new file mode 100644
index 00000000000..f32d1c35a7a
--- /dev/null
+++ b/handwritten/nodejs-logging-bunyan/tsconfig.json
@@ -0,0 +1,16 @@
+{
+ "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..5dc5c58f93f 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-bunyan": {},
"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