Skip to main content
Glama
logging-utils.js16.1 kB
"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

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tianpeijun/email-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server