refactor(api): Central Logging 📜 (#1348)

* WIP: initial logging changes
add several transports in ~/config/winston
omit messages in logs, truncate long strings
add short blurb in dotenv for debug logging
GoogleClient: using logger
OpenAIClient: using logger, handleOpenAIErrors
Adding typedef for payload message
bumped winston and using winston-daily-rotate-file
moved config for server paths to ~/config dir
Added `DEBUG_LOGGING=true` to .env.example

* WIP: Refactor logging statements in code

* WIP: Refactor logging statements and import configurations

* WIP: Refactor logging statements and import configurations

* refactor: broadcast Redis initialization message with `info` not `debug`

* refactor: complete Refactor logging statements and import configurations

* chore: delete unused tools

* fix: circular dependencies due to accessing logger

* refactor(handleText): handle booleans and write tests

* refactor: redact sensitive values, better formatting

* chore: improve log formatting, avoid passing strings to 2nd arg

* fix(ci): fix jest tests due to logger changes

* refactor(getAvailablePluginsController): cache plugins as they are static and avoids async addOpenAPISpecs call every time

* chore: update docs

* chore: update docs

* chore: create separate meiliSync logger, clean up logs to avoid being unnecessarily verbose

* chore: spread objects where they are commonly logged to allow string truncation

* chore: improve error log formatting
This commit is contained in:
Danny Avila 2023-12-14 07:49:27 -05:00 committed by GitHub
parent 49571ac635
commit ea1dd59ef4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
115 changed files with 1271 additions and 1001 deletions

View file

@ -1,6 +1,7 @@
const mongoose = require('mongoose');
const balanceSchema = require('./schema/balance');
const { getMultiplier } = require('./tx');
const { logger } = require('~/config');
balanceSchema.statics.check = async function ({
user,
@ -9,25 +10,21 @@ balanceSchema.statics.check = async function ({
valueKey,
tokenType,
amount,
debug,
}) {
const multiplier = getMultiplier({ valueKey, tokenType, model, endpoint });
const tokenCost = amount * multiplier;
const { tokenCredits: balance } = (await this.findOne({ user }, 'tokenCredits').lean()) ?? {};
if (debug) {
console.log('balance check', {
user,
model,
endpoint,
valueKey,
tokenType,
amount,
debug,
balance,
multiplier,
});
}
logger.debug('[Balance.check]', {
user,
model,
endpoint,
valueKey,
tokenType,
amount,
balance,
multiplier,
});
if (!balance) {
return {
@ -37,9 +34,7 @@ balanceSchema.statics.check = async function ({
};
}
if (debug) {
console.log('balance check', { tokenCost });
}
logger.debug('[Balance.check]', { tokenCost });
return { canSpend: balance >= tokenCost, balance, tokenCost };
};

View file

@ -1,4 +1,6 @@
const mongoose = require('mongoose');
const { logger } = require('~/config');
const major = [0, 0];
const minor = [0, 0];
const patch = [0, 5];
@ -69,7 +71,7 @@ module.exports = {
try {
return await Config.find(filter).lean();
} catch (error) {
console.error(error);
logger.error('Error getting configs', error);
return { config: 'Error getting configs' };
}
},
@ -77,7 +79,7 @@ module.exports = {
try {
return await Config.deleteMany(filter);
} catch (error) {
console.error(error);
logger.error('Error deleting configs', error);
return { config: 'Error deleting configs' };
}
},

View file

@ -1,12 +1,12 @@
// const { Conversation } = require('./plugins');
const Conversation = require('./schema/convoSchema');
const { getMessages, deleteMessages } = require('./Message');
const logger = require('~/config/winston');
const getConvo = async (user, conversationId) => {
try {
return await Conversation.findOne({ user, conversationId }).lean();
} catch (error) {
console.log(error);
logger.error('[getConvo] Error getting single conversation', error);
return { message: 'Error getting single conversation' };
}
};
@ -26,7 +26,7 @@ module.exports = {
upsert: true,
});
} catch (error) {
console.log(error);
logger.error('[saveConvo] Error saving conversation', error);
return { message: 'Error saving conversation' };
}
},
@ -41,7 +41,7 @@ module.exports = {
.lean();
return { conversations: convos, pages: totalPages, pageNumber, pageSize };
} catch (error) {
console.log(error);
logger.error('[getConvosByPage] Error getting conversations', error);
return { message: 'Error getting conversations' };
}
},
@ -87,7 +87,7 @@ module.exports = {
convoMap,
};
} catch (error) {
console.log(error);
logger.error('[getConvosQueried] Error getting conversations', error);
return { message: 'Error fetching conversations' };
}
},
@ -104,7 +104,7 @@ module.exports = {
return convo?.title || 'New Chat';
}
} catch (error) {
console.log(error);
logger.error('[getConvoTitle] Error getting conversation title', error);
return { message: 'Error getting conversation title' };
}
},
@ -123,7 +123,7 @@ module.exports = {
* const user = 'someUserId';
* const filter = { someField: 'someValue' };
* const result = await deleteConvos(user, filter);
* console.log(result); // { n: 5, ok: 1, deletedCount: 5, messages: { n: 10, ok: 1, deletedCount: 10 } }
* logger.error(result); // { n: 5, ok: 1, deletedCount: 5, messages: { n: 10, ok: 1, deletedCount: 10 } }
*/
deleteConvos: async (user, filter) => {
let toRemove = await Conversation.find({ ...filter, user }).select('conversationId');

View file

@ -1,5 +1,6 @@
const { z } = require('zod');
const Message = require('./schema/messageSchema');
const logger = require('~/config/winston');
const idSchema = z.string().uuid();
@ -67,7 +68,7 @@ module.exports = {
tokenCount,
};
} catch (err) {
console.error(`Error saving message: ${err}`);
logger.error('Error saving message:', err);
throw new Error('Failed to save message.');
}
},
@ -92,7 +93,7 @@ module.exports = {
isEdited: true,
};
} catch (err) {
console.error(`Error updating message: ${err}`);
logger.error('Error updating message:', err);
throw new Error('Failed to update message.');
}
},
@ -106,7 +107,7 @@ module.exports = {
});
}
} catch (err) {
console.error(`Error deleting messages: ${err}`);
logger.error('Error deleting messages:', err);
throw new Error('Failed to delete messages.');
}
},
@ -115,7 +116,7 @@ module.exports = {
try {
return await Message.find(filter).sort({ createdAt: 1 }).lean();
} catch (err) {
console.error(`Error getting messages: ${err}`);
logger.error('Error getting messages:', err);
throw new Error('Failed to get messages.');
}
},
@ -124,7 +125,7 @@ module.exports = {
try {
return await Message.deleteMany(filter);
} catch (err) {
console.error(`Error deleting messages: ${err}`);
logger.error('Error deleting messages:', err);
throw new Error('Failed to delete messages.');
}
},

View file

@ -1,10 +1,11 @@
const Preset = require('./schema/presetSchema');
const { logger } = require('~/config');
const getPreset = async (user, presetId) => {
try {
return await Preset.findOne({ user, presetId }).lean();
} catch (error) {
console.log(error);
logger.error('[getPreset] Error getting single preset', error);
return { message: 'Error getting single preset' };
}
};
@ -30,7 +31,7 @@ module.exports = {
return presets;
} catch (error) {
console.log(error);
logger.error('[getPresets] Error getting presets', error);
return { message: 'Error retrieving presets' };
}
},
@ -62,7 +63,7 @@ module.exports = {
setter.$set = update;
return await Preset.findOneAndUpdate({ presetId, user }, setter, { new: true, upsert: true });
} catch (error) {
console.log(error);
logger.error('[savePreset] Error saving preset', error);
return { message: 'Error saving preset' };
}
},

View file

@ -1,4 +1,5 @@
const mongoose = require('mongoose');
const { logger } = require('~/config');
const promptSchema = mongoose.Schema(
{
@ -28,7 +29,7 @@ module.exports = {
});
return { title, prompt };
} catch (error) {
console.error(error);
logger.error('Error saving prompt', error);
return { prompt: 'Error saving prompt' };
}
},
@ -36,7 +37,7 @@ module.exports = {
try {
return await Prompt.find(filter).lean();
} catch (error) {
console.error(error);
logger.error('Error getting prompts', error);
return { prompt: 'Error getting prompts' };
}
},
@ -44,7 +45,7 @@ module.exports = {
try {
return await Prompt.deleteMany(filter);
} catch (error) {
console.error(error);
logger.error('Error deleting prompts', error);
return { prompt: 'Error deleting prompts' };
}
},

View file

@ -1,6 +1,8 @@
const mongoose = require('mongoose');
const crypto = require('crypto');
const signPayload = require('../server/services/signPayload');
const mongoose = require('mongoose');
const signPayload = require('~/server/services/signPayload');
const { logger } = require('~/config');
const { REFRESH_TOKEN_EXPIRY } = process.env ?? {};
const expires = eval(REFRESH_TOKEN_EXPIRY) ?? 1000 * 60 * 60 * 24 * 7;
@ -44,8 +46,8 @@ sessionSchema.methods.generateRefreshToken = async function () {
return refreshToken;
} catch (error) {
console.error(
'Error generating refresh token. Have you set a JWT_REFRESH_SECRET in the .env file?\n\n',
logger.error(
'Error generating refresh token. Is a `JWT_REFRESH_SECRET` set in the .env file?\n\n',
error,
);
throw error;
@ -59,10 +61,12 @@ sessionSchema.statics.deleteAllUserSessions = async function (userId) {
}
const result = await this.deleteMany({ user: userId });
if (result && result?.deletedCount > 0) {
console.log(`Deleted ${result.deletedCount} sessions for user ${userId}.`);
logger.debug(
`[deleteAllUserSessions] Deleted ${result.deletedCount} sessions for user ${userId}.`,
);
}
} catch (error) {
console.log('Error in deleting user sessions:', error);
logger.error('[deleteAllUserSessions] Error in deleting user sessions:', error);
throw error;
}
};

View file

@ -13,7 +13,6 @@ const { logViolation } = require('../cache');
* @param {string} params.txData.user - The user ID or identifier.
* @param {('prompt' | 'completion')} params.txData.tokenType - The type of token.
* @param {number} params.txData.amount - The amount of tokens.
* @param {boolean} params.txData.debug - Debug flag.
* @param {string} params.txData.model - The model name or identifier.
* @returns {Promise<boolean>} Returns true if the user can spend the amount, otherwise denies the request.
* @throws {Error} Throws an error if there's an issue with the balance check.

View file

@ -1,7 +1,9 @@
const _ = require('lodash');
const mongoose = require('mongoose');
const { MeiliSearch } = require('meilisearch');
const { cleanUpPrimaryKeyValue } = require('../../lib/utils/misc');
const _ = require('lodash');
const { cleanUpPrimaryKeyValue } = require('~/lib/utils/misc');
const logger = require('~/config/meiliLogger');
const searchEnabled = process.env.SEARCH && process.env.SEARCH.toLowerCase() === 'true';
const meiliEnabled = process.env.MEILI_HOST && process.env.MEILI_MASTER_KEY && searchEnabled;
@ -64,8 +66,7 @@ const createMeiliMongooseModel = function ({ index, attributesToIndex }) {
offset += batchSize;
}
console.log('indexMap', indexMap.size);
console.log('mongoMap', mongoMap.size);
logger.debug('[syncWithMeili]', { indexMap: indexMap.size, mongoMap: mongoMap.size });
const updateOps = [];
@ -80,7 +81,11 @@ const createMeiliMongooseModel = function ({ index, attributesToIndex }) {
(doc.text && doc.text !== mongoMap.get(id).text) ||
(doc.title && doc.title !== mongoMap.get(id).title)
) {
console.log(`${id} had document discrepancy in ${doc.text ? 'text' : 'title'} field`);
logger.debug(
`[syncWithMeili] ${id} had document discrepancy in ${
doc.text ? 'text' : 'title'
} field`,
);
updateOps.push({
updateOne: { filter: update, update: { $set: { _meiliIndex: true } } },
});
@ -116,15 +121,14 @@ const createMeiliMongooseModel = function ({ index, attributesToIndex }) {
if (updateOps.length > 0) {
await this.collection.bulkWrite(updateOps);
console.log(
`[Meilisearch] Finished indexing ${
logger.debug(
`[syncWithMeili] Finished indexing ${
primaryKey === 'messageId' ? 'messages' : 'conversations'
}`,
);
}
} catch (error) {
console.log('[Meilisearch] Error adding document to Meili');
console.error(error);
logger.error('[syncWithMeili] Error adding document to Meili', error);
}
}
@ -143,7 +147,7 @@ const createMeiliMongooseModel = function ({ index, attributesToIndex }) {
const query = {};
// query[primaryKey] = { $in: _.map(data.hits, primaryKey) };
query[primaryKey] = _.map(data.hits, (hit) => cleanUpPrimaryKeyValue(hit[primaryKey]));
// console.log('query', query);
// logger.debug('query', query);
const hitsFromMongoose = await this.find(
query,
_.reduce(
@ -186,11 +190,11 @@ const createMeiliMongooseModel = function ({ index, attributesToIndex }) {
async addObjectToMeili() {
const object = this.preprocessObjectForIndex();
try {
// console.log('Adding document to Meili', object);
// logger.debug('Adding document to Meili', object);
await index.addDocuments([object]);
} catch (error) {
// console.log('Error adding document to Meili');
// console.error(error);
// logger.debug('Error adding document to Meili');
// logger.error(error);
}
await this.collection.updateMany({ _id: this._id }, { $set: { _meiliIndex: true } });
@ -311,10 +315,10 @@ module.exports = function mongoMeili(schema, options) {
return next();
} catch (error) {
if (meiliEnabled) {
console.log(
'[Meilisearch] There was an issue deleting conversation indexes upon deletion, next startup may be slow due to syncing',
logger.error(
'[MeiliMongooseModel.deleteMany] There was an issue deleting conversation indexes upon deletion, next startup may be slow due to syncing',
error,
);
console.error(error);
}
return next();
}
@ -335,7 +339,11 @@ module.exports = function mongoMeili(schema, options) {
try {
meiliDoc = await client.index('convos').getDocument(doc.conversationId);
} catch (error) {
console.log('[Meilisearch] Convo not found and will index', doc.conversationId);
logger.error(
'[MeiliMongooseModel.findOneAndUpdate] Convo not found in MeiliSearch and will index ' +
doc.conversationId,
error,
);
}
}

View file

@ -1,5 +1,5 @@
const mongoose = require('mongoose');
const mongoMeili = require('../plugins/mongoMeili');
const mongoMeili = require('~/models/plugins/mongoMeili');
const messageSchema = mongoose.Schema(
{
messageId: {

View file

@ -1,4 +1,5 @@
const Transaction = require('./Transaction');
const { logger } = require('~/config');
/**
* Creates up to two transactions to record the spending of tokens.
@ -30,7 +31,7 @@ const spendTokens = async (txData, tokenUsage) => {
}
if (!completionTokens) {
this.debug && console.dir({ prompt, completion }, { depth: null });
logger.debug('[spendTokens] !completionTokens', { prompt, completion });
return;
}
@ -40,9 +41,9 @@ const spendTokens = async (txData, tokenUsage) => {
rawAmount: -completionTokens,
});
this.debug && console.dir({ prompt, completion }, { depth: null });
logger.debug('[spendTokens] post-transaction', { prompt, completion });
} catch (err) {
console.error(err);
logger.error('[spendTokens]', err);
}
};