init
This commit is contained in:
+80
@@ -0,0 +1,80 @@
|
||||
"use strict";
|
||||
// Copyright 2024 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.
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Colours = void 0;
|
||||
/**
|
||||
* Handles figuring out if we can use ANSI colours and handing out the escape codes.
|
||||
*
|
||||
* This is for package-internal use only, and may change at any time.
|
||||
*
|
||||
* @private
|
||||
* @internal
|
||||
*/
|
||||
class Colours {
|
||||
/**
|
||||
* @param stream The stream (e.g. process.stderr)
|
||||
* @returns true if the stream should have colourization enabled
|
||||
*/
|
||||
static isEnabled(stream) {
|
||||
return (stream.isTTY &&
|
||||
(typeof stream.getColorDepth === 'function'
|
||||
? stream.getColorDepth() > 2
|
||||
: true));
|
||||
}
|
||||
static refresh() {
|
||||
Colours.enabled = Colours.isEnabled(process.stderr);
|
||||
if (!this.enabled) {
|
||||
Colours.reset = '';
|
||||
Colours.bright = '';
|
||||
Colours.dim = '';
|
||||
Colours.red = '';
|
||||
Colours.green = '';
|
||||
Colours.yellow = '';
|
||||
Colours.blue = '';
|
||||
Colours.magenta = '';
|
||||
Colours.cyan = '';
|
||||
Colours.white = '';
|
||||
Colours.grey = '';
|
||||
}
|
||||
else {
|
||||
Colours.reset = '\u001b[0m';
|
||||
Colours.bright = '\u001b[1m';
|
||||
Colours.dim = '\u001b[2m';
|
||||
Colours.red = '\u001b[31m';
|
||||
Colours.green = '\u001b[32m';
|
||||
Colours.yellow = '\u001b[33m';
|
||||
Colours.blue = '\u001b[34m';
|
||||
Colours.magenta = '\u001b[35m';
|
||||
Colours.cyan = '\u001b[36m';
|
||||
Colours.white = '\u001b[37m';
|
||||
Colours.grey = '\u001b[90m';
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.Colours = Colours;
|
||||
Colours.enabled = false;
|
||||
Colours.reset = '';
|
||||
Colours.bright = '';
|
||||
Colours.dim = '';
|
||||
Colours.red = '';
|
||||
Colours.green = '';
|
||||
Colours.yellow = '';
|
||||
Colours.blue = '';
|
||||
Colours.magenta = '';
|
||||
Colours.cyan = '';
|
||||
Colours.white = '';
|
||||
Colours.grey = '';
|
||||
Colours.refresh();
|
||||
//# sourceMappingURL=colours.js.map
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
// Copyright 2024 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.
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__exportStar(require("./logging-utils"), exports);
|
||||
//# sourceMappingURL=index.js.map
|
||||
+406
@@ -0,0 +1,406 @@
|
||||
"use strict";
|
||||
// Copyright 2021-2024 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.
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.env = exports.DebugLogBackendBase = exports.placeholder = exports.AdhocDebugLogger = exports.LogSeverity = void 0;
|
||||
exports.getNodeBackend = getNodeBackend;
|
||||
exports.getDebugBackend = getDebugBackend;
|
||||
exports.getStructuredBackend = getStructuredBackend;
|
||||
exports.setBackend = setBackend;
|
||||
exports.log = log;
|
||||
const node_events_1 = require("node:events");
|
||||
const process = __importStar(require("node:process"));
|
||||
const util = __importStar(require("node:util"));
|
||||
const colours_1 = require("./colours");
|
||||
// Some functions (as noted) are based on the Node standard library, from
|
||||
// the following file:
|
||||
//
|
||||
// https://github.com/nodejs/node/blob/main/lib/internal/util/debuglog.js
|
||||
/**
|
||||
* This module defines an ad-hoc debug logger for Google Cloud Platform
|
||||
* client libraries in Node. An ad-hoc debug logger is a tool which lets
|
||||
* users use an external, unified interface (in this case, environment
|
||||
* variables) to determine what logging they want to see at runtime. This
|
||||
* isn't necessarily fed into the console, but is meant to be under the
|
||||
* control of the user. The kind of logging that will be produced by this
|
||||
* is more like "call retry happened", not "event you'd want to record
|
||||
* in Cloud Logger".
|
||||
*
|
||||
* More for Googlers implementing libraries with it:
|
||||
* go/cloud-client-logging-design
|
||||
*/
|
||||
/**
|
||||
* Possible log levels. These are a subset of Cloud Observability levels.
|
||||
* https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity
|
||||
*/
|
||||
var LogSeverity;
|
||||
(function (LogSeverity) {
|
||||
LogSeverity["DEFAULT"] = "DEFAULT";
|
||||
LogSeverity["DEBUG"] = "DEBUG";
|
||||
LogSeverity["INFO"] = "INFO";
|
||||
LogSeverity["WARNING"] = "WARNING";
|
||||
LogSeverity["ERROR"] = "ERROR";
|
||||
})(LogSeverity || (exports.LogSeverity = LogSeverity = {}));
|
||||
/**
|
||||
* Our logger instance. This actually contains the meat of dealing
|
||||
* with log lines, including EventEmitter. This contains the function
|
||||
* that will be passed back to users of the package.
|
||||
*/
|
||||
class AdhocDebugLogger extends node_events_1.EventEmitter {
|
||||
/**
|
||||
* @param upstream The backend will pass a function that will be
|
||||
* called whenever our logger function is invoked.
|
||||
*/
|
||||
constructor(namespace, upstream) {
|
||||
super();
|
||||
this.namespace = namespace;
|
||||
this.upstream = upstream;
|
||||
this.func = Object.assign(this.invoke.bind(this), {
|
||||
// Also add an instance pointer back to us.
|
||||
instance: this,
|
||||
// And pull over the EventEmitter functionality.
|
||||
on: (event, listener) => this.on(event, listener),
|
||||
});
|
||||
// Convenience methods for log levels.
|
||||
this.func.debug = (...args) => this.invokeSeverity(LogSeverity.DEBUG, ...args);
|
||||
this.func.info = (...args) => this.invokeSeverity(LogSeverity.INFO, ...args);
|
||||
this.func.warn = (...args) => this.invokeSeverity(LogSeverity.WARNING, ...args);
|
||||
this.func.error = (...args) => this.invokeSeverity(LogSeverity.ERROR, ...args);
|
||||
this.func.sublog = (namespace) => log(namespace, this.func);
|
||||
}
|
||||
invoke(fields, ...args) {
|
||||
// Push out any upstream logger first.
|
||||
if (this.upstream) {
|
||||
this.upstream(fields, ...args);
|
||||
}
|
||||
// Emit sink events.
|
||||
this.emit('log', fields, args);
|
||||
}
|
||||
invokeSeverity(severity, ...args) {
|
||||
this.invoke({ severity }, ...args);
|
||||
}
|
||||
}
|
||||
exports.AdhocDebugLogger = AdhocDebugLogger;
|
||||
/**
|
||||
* This can be used in place of a real logger while waiting for Promises or disabling logging.
|
||||
*/
|
||||
exports.placeholder = new AdhocDebugLogger('', () => { }).func;
|
||||
/**
|
||||
* The base class for debug logging backends. It's possible to use this, but the
|
||||
* same non-guarantees above still apply (unstable interface, etc).
|
||||
*
|
||||
* @private
|
||||
* @internal
|
||||
*/
|
||||
class DebugLogBackendBase {
|
||||
constructor() {
|
||||
var _a;
|
||||
this.cached = new Map();
|
||||
this.filters = [];
|
||||
this.filtersSet = false;
|
||||
// Look for the Node config variable for what systems to enable. We'll store
|
||||
// these for the log method below, which will call setFilters() once.
|
||||
let nodeFlag = (_a = process.env[exports.env.nodeEnables]) !== null && _a !== void 0 ? _a : '*';
|
||||
if (nodeFlag === 'all') {
|
||||
nodeFlag = '*';
|
||||
}
|
||||
this.filters = nodeFlag.split(',');
|
||||
}
|
||||
log(namespace, fields, ...args) {
|
||||
try {
|
||||
if (!this.filtersSet) {
|
||||
this.setFilters();
|
||||
this.filtersSet = true;
|
||||
}
|
||||
let logger = this.cached.get(namespace);
|
||||
if (!logger) {
|
||||
logger = this.makeLogger(namespace);
|
||||
this.cached.set(namespace, logger);
|
||||
}
|
||||
logger(fields, ...args);
|
||||
}
|
||||
catch (e) {
|
||||
// Silently ignore all errors; we don't want them to interfere with
|
||||
// the user's running app.
|
||||
// e;
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.DebugLogBackendBase = DebugLogBackendBase;
|
||||
// The basic backend. This one definitely works, but it's less feature-filled.
|
||||
//
|
||||
// Rather than using util.debuglog, this implements the same basic logic directly.
|
||||
// The reason for this decision is that debuglog checks the value of the
|
||||
// NODE_DEBUG environment variable before any user code runs; we therefore
|
||||
// can't pipe our own enables into it (and util.debuglog will never print unless
|
||||
// the user duplicates it into NODE_DEBUG, which isn't reasonable).
|
||||
//
|
||||
class NodeBackend extends DebugLogBackendBase {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
// Default to allowing all systems, since we gate earlier based on whether the
|
||||
// variable is empty.
|
||||
this.enabledRegexp = /.*/g;
|
||||
}
|
||||
isEnabled(namespace) {
|
||||
return this.enabledRegexp.test(namespace);
|
||||
}
|
||||
makeLogger(namespace) {
|
||||
if (!this.enabledRegexp.test(namespace)) {
|
||||
return () => { };
|
||||
}
|
||||
return (fields, ...args) => {
|
||||
var _a;
|
||||
// TODO: `fields` needs to be turned into a string here, one way or another.
|
||||
const nscolour = `${colours_1.Colours.green}${namespace}${colours_1.Colours.reset}`;
|
||||
const pid = `${colours_1.Colours.yellow}${process.pid}${colours_1.Colours.reset}`;
|
||||
let level;
|
||||
switch (fields.severity) {
|
||||
case LogSeverity.ERROR:
|
||||
level = `${colours_1.Colours.red}${fields.severity}${colours_1.Colours.reset}`;
|
||||
break;
|
||||
case LogSeverity.INFO:
|
||||
level = `${colours_1.Colours.magenta}${fields.severity}${colours_1.Colours.reset}`;
|
||||
break;
|
||||
case LogSeverity.WARNING:
|
||||
level = `${colours_1.Colours.yellow}${fields.severity}${colours_1.Colours.reset}`;
|
||||
break;
|
||||
default:
|
||||
level = (_a = fields.severity) !== null && _a !== void 0 ? _a : LogSeverity.DEFAULT;
|
||||
break;
|
||||
}
|
||||
const msg = util.formatWithOptions({ colors: colours_1.Colours.enabled }, ...args);
|
||||
const filteredFields = Object.assign({}, fields);
|
||||
delete filteredFields.severity;
|
||||
const fieldsJson = Object.getOwnPropertyNames(filteredFields).length
|
||||
? JSON.stringify(filteredFields)
|
||||
: '';
|
||||
const fieldsColour = fieldsJson
|
||||
? `${colours_1.Colours.grey}${fieldsJson}${colours_1.Colours.reset}`
|
||||
: '';
|
||||
console.error('%s [%s|%s] %s%s', pid, nscolour, level, msg, fieldsJson ? ` ${fieldsColour}` : '');
|
||||
};
|
||||
}
|
||||
// Regexp patterns below are from here:
|
||||
// https://github.com/nodejs/node/blob/c0aebed4b3395bd65d54b18d1fd00f071002ac20/lib/internal/util/debuglog.js#L36
|
||||
setFilters() {
|
||||
const totalFilters = this.filters.join(',');
|
||||
const regexp = totalFilters
|
||||
.replace(/[|\\{}()[\]^$+?.]/g, '\\$&')
|
||||
.replace(/\*/g, '.*')
|
||||
.replace(/,/g, '$|^');
|
||||
this.enabledRegexp = new RegExp(`^${regexp}$`, 'i');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @returns A backend based on Node util.debuglog; this is the default.
|
||||
*/
|
||||
function getNodeBackend() {
|
||||
return new NodeBackend();
|
||||
}
|
||||
class DebugBackend extends DebugLogBackendBase {
|
||||
constructor(pkg) {
|
||||
super();
|
||||
this.debugPkg = pkg;
|
||||
}
|
||||
makeLogger(namespace) {
|
||||
const debugLogger = this.debugPkg(namespace);
|
||||
return (fields, ...args) => {
|
||||
// TODO: `fields` needs to be turned into a string here.
|
||||
debugLogger(args[0], ...args.slice(1));
|
||||
};
|
||||
}
|
||||
setFilters() {
|
||||
var _a;
|
||||
const existingFilters = (_a = process.env['NODE_DEBUG']) !== null && _a !== void 0 ? _a : '';
|
||||
process.env['NODE_DEBUG'] = `${existingFilters}${existingFilters ? ',' : ''}${this.filters.join(',')}`;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates a "debug" package backend. The user must call require('debug') and pass
|
||||
* the resulting object to this function.
|
||||
*
|
||||
* ```
|
||||
* setBackend(getDebugBackend(require('debug')))
|
||||
* ```
|
||||
*
|
||||
* https://www.npmjs.com/package/debug
|
||||
*
|
||||
* Note: Google does not explicitly endorse or recommend this package; it's just
|
||||
* being provided as an option.
|
||||
*
|
||||
* @returns A backend based on the npm "debug" package.
|
||||
*/
|
||||
function getDebugBackend(debugPkg) {
|
||||
return new DebugBackend(debugPkg);
|
||||
}
|
||||
/**
|
||||
* This pretty much works like the Node logger, but it outputs structured
|
||||
* logging JSON matching Google Cloud's ingestion specs. Rather than handling
|
||||
* its own output, it wraps another backend. The passed backend must be a subclass
|
||||
* of `DebugLogBackendBase` (any of the backends exposed by this package will work).
|
||||
*/
|
||||
class StructuredBackend extends DebugLogBackendBase {
|
||||
constructor(upstream) {
|
||||
var _a;
|
||||
super();
|
||||
this.upstream = (_a = upstream) !== null && _a !== void 0 ? _a : new NodeBackend();
|
||||
}
|
||||
makeLogger(namespace) {
|
||||
const debugLogger = this.upstream.makeLogger(namespace);
|
||||
return (fields, ...args) => {
|
||||
var _a;
|
||||
const severity = (_a = fields.severity) !== null && _a !== void 0 ? _a : LogSeverity.INFO;
|
||||
const json = Object.assign({
|
||||
severity,
|
||||
message: util.format(...args),
|
||||
}, fields);
|
||||
const jsonString = JSON.stringify(json);
|
||||
debugLogger(fields, jsonString);
|
||||
};
|
||||
}
|
||||
setFilters() {
|
||||
this.upstream.setFilters();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates a "structured logging" backend. This pretty much works like the
|
||||
* Node logger, but it outputs structured logging JSON matching Google
|
||||
* Cloud's ingestion specs instead of plain text.
|
||||
*
|
||||
* ```
|
||||
* setBackend(getStructuredBackend())
|
||||
* ```
|
||||
*
|
||||
* @param upstream If you want to use something besides the Node backend to
|
||||
* write the actual log lines into, pass that here.
|
||||
* @returns A backend based on Google Cloud structured logging.
|
||||
*/
|
||||
function getStructuredBackend(upstream) {
|
||||
return new StructuredBackend(upstream);
|
||||
}
|
||||
/**
|
||||
* The environment variables that we standardized on, for all ad-hoc logging.
|
||||
*/
|
||||
exports.env = {
|
||||
/**
|
||||
* Filter wildcards specific to the Node syntax, and similar to the built-in
|
||||
* utils.debuglog() environment variable. If missing, disables logging.
|
||||
*/
|
||||
nodeEnables: 'GOOGLE_SDK_NODE_LOGGING',
|
||||
};
|
||||
// Keep a copy of all namespaced loggers so users can reliably .on() them.
|
||||
// Note that these cached functions will need to deal with changes in the backend.
|
||||
const loggerCache = new Map();
|
||||
// Our current global backend. This might be:
|
||||
let cachedBackend = undefined;
|
||||
/**
|
||||
* Set the backend to use for our log output.
|
||||
* - A backend object
|
||||
* - null to disable logging
|
||||
* - undefined for "nothing yet", defaults to the Node backend
|
||||
*
|
||||
* @param backend Results from one of the get*Backend() functions.
|
||||
*/
|
||||
function setBackend(backend) {
|
||||
cachedBackend = backend;
|
||||
loggerCache.clear();
|
||||
}
|
||||
/**
|
||||
* Creates a logging function. Multiple calls to this with the same namespace
|
||||
* will produce the same logger, with the same event emitter hooks.
|
||||
*
|
||||
* Namespaces can be a simple string ("system" name), or a qualified string
|
||||
* (system:subsystem), which can be used for filtering, or for "system:*".
|
||||
*
|
||||
* @param namespace The namespace, a descriptive text string.
|
||||
* @returns A function you can call that works similar to console.log().
|
||||
*/
|
||||
function log(namespace, parent) {
|
||||
// If the enable flag isn't set, do nothing.
|
||||
const enablesFlag = process.env[exports.env.nodeEnables];
|
||||
if (!enablesFlag) {
|
||||
return exports.placeholder;
|
||||
}
|
||||
// This might happen mostly if the typings are dropped in a user's code,
|
||||
// or if they're calling from JavaScript.
|
||||
if (!namespace) {
|
||||
return exports.placeholder;
|
||||
}
|
||||
// Handle sub-loggers.
|
||||
if (parent) {
|
||||
namespace = `${parent.instance.namespace}:${namespace}`;
|
||||
}
|
||||
// Reuse loggers so things like event sinks are persistent.
|
||||
const existing = loggerCache.get(namespace);
|
||||
if (existing) {
|
||||
return existing.func;
|
||||
}
|
||||
// Do we have a backend yet?
|
||||
if (cachedBackend === null) {
|
||||
// Explicitly disabled.
|
||||
return exports.placeholder;
|
||||
}
|
||||
else if (cachedBackend === undefined) {
|
||||
// One hasn't been made yet, so default to Node.
|
||||
cachedBackend = getNodeBackend();
|
||||
}
|
||||
// The logger is further wrapped so we can handle the backend changing out.
|
||||
const logger = (() => {
|
||||
let previousBackend = undefined;
|
||||
const newLogger = new AdhocDebugLogger(namespace, (fields, ...args) => {
|
||||
if (previousBackend !== cachedBackend) {
|
||||
// Did the user pass a custom backend?
|
||||
if (cachedBackend === null) {
|
||||
// Explicitly disabled.
|
||||
return;
|
||||
}
|
||||
else if (cachedBackend === undefined) {
|
||||
// One hasn't been made yet, so default to Node.
|
||||
cachedBackend = getNodeBackend();
|
||||
}
|
||||
previousBackend = cachedBackend;
|
||||
}
|
||||
cachedBackend === null || cachedBackend === void 0 ? void 0 : cachedBackend.log(namespace, fields, ...args);
|
||||
});
|
||||
return newLogger;
|
||||
})();
|
||||
loggerCache.set(namespace, logger);
|
||||
return logger.func;
|
||||
}
|
||||
//# sourceMappingURL=logging-utils.js.map
|
||||
Reference in New Issue
Block a user