Skip to main content
Glama
logger.ts3.91 kB
/* eslint-disable consistent-return,no-console */ import _ from "lodash"; import Debug from "debug"; import PrettyError from "pretty-error"; import chalk from "chalk"; import { ApiError } from "./api-error"; // import { getLogDnaLogger } from '../external-services/logdna'; // import { logErrorToSentry } from '../external-services/sentry'; // const logDnaLogger = getLogDnaLogger(); // // pass through errors with the logger itself to sentry // if (logDnaLogger) { // logDnaLogger.on('error', (err) => { // console.error('ERROR WITH LOGGER', err); // logErrorToSentry(null, null, err); // }); // } // add some fancy formatting to errors in the console // and skip some packages/lines that aren't helpful // TODO: add more formatting rules? make prettier? const prettyError = new PrettyError(); prettyError.skipNodeFiles(); prettyError.skipPackage( "koa", "@koa", "@koa/router", "koa-compose", "koa-body", ); // used to toggle swallowing 500 errors in test mode - only when testing 500 handling on purpose... let swallowUnexpectedErrors = false; export function setSwallowErrors(newValue: boolean) { swallowUnexpectedErrors = newValue; } const debuggers: Record<string, Debug.Debugger> = {}; // general logger function that writes a log to console (using debug), logdna, and sentry (if an error) // note this should not be used directly in http routes or queues, instead using ctx.log which includes extra metadata export function log( message: string, meta: { type?: string; message?: string; statusCode?: number; error?: Error | ApiError | any; [key: string]: any; } = {}, ) { if (!_.isString(message)) throw new Error("Missing log message"); if (meta === null) meta = {}; if (meta.id) throw new Error("Do not set ID in data to log!"); const error = meta.error; if (error) { if (meta.message) meta.message += ` - ${error.name}`; // replace the raw error with something better for logging if (error) { meta.error = _.pickBy( _.pick(error, [ // extra _.pickBy removes empty keys (details) "name", "generalType", "message", "details", "stack", ]), ); // if expected error, trim off stack if (meta.statusCode && meta.statusCode < 500) { meta.error = _.omit(meta.error, ["stack"]); } } // if the error looks like an http request, we add it so it gets captured if (error.response) { meta.error.httpResponse = JSON.stringify(error.response.data); } } // TODO: check meta for data that is too deep or circular. It should be kept simple! // we use meta.type both to categorize logs on logdna and also as a debug "topic" // meta.type defaults to "general" if nothing is set meta.type = meta.type || "general"; // log to console using debug with the topic set by meta.type const debugTopic = meta.type; if (!debuggers[debugTopic]) debuggers[debugTopic] = Debug(debugTopic); const debugLogger = debuggers[debugTopic]; debugLogger(message); const metaWithoutType = _.omit(meta, "type"); if (!_.isEmpty(metaWithoutType)) debugLogger(metaWithoutType); // maybe dont always want to show? // log to LogDNA // if (logDnaLogger) { // logDnaLogger.log(message, { // level: (error ? "error" : "info") as any, // meta, // }); // } // only unexpected errors get passed through to sentry if ( error && error instanceof Error && !(error as ApiError).expectedError && !swallowUnexpectedErrors ) { console.log(chalk.red("------ CAUGHT EXCEPTION ------")); console.log(prettyError.render(error)); console.log(chalk.red("------------------------------")); // logErrorToSentry(_.pick(meta, "url", "method"), meta.user, error); } } // async function flush() { // // TODO: probably want to flush logs before shutdown // }

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/systeminit/si'

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