WIP add logging

This commit is contained in:
shatfield4 2024-02-02 16:49:04 -08:00
parent e95d547be6
commit 0b313e7078
13 changed files with 151 additions and 40 deletions

View File

@ -161,7 +161,7 @@ export default function SettingsSidebar() {
icon={<Notepad className="h-5 w-5 flex-shrink-0" />}
user={user}
flex={true}
allowedRole={["admin", "manager"]}
allowedRole={["admin"]}
/>
</div>
</div>

View File

@ -389,6 +389,18 @@ const System = {
return [];
});
},
logs: async (offset = 0) => {
return await fetch(`${API_BASE}/system/logs`, {
method: "POST",
headers: baseHeaders(),
body: JSON.stringify({ offset }),
})
.then((res) => res.json())
.catch((e) => {
console.error(e);
return [];
});
},
deleteChat: async (chatId) => {
return await fetch(`${API_BASE}/system/workspace-chats/${chatId}`, {
method: "DELETE",

View File

@ -12,6 +12,7 @@ const {
findDocumentInDocuments,
} = require("../../../utils/files");
const { reqBody } = require("../../../utils/http");
const { EventLogs } = require("../../../models/eventLogs");
const { handleUploads } = setupMulter();
function apiDocumentEndpoints(app) {
@ -22,7 +23,7 @@ function apiDocumentEndpoints(app) {
[validApiKey],
handleUploads.single("file"),
async (request, response) => {
/*
/*
#swagger.tags = ['Documents']
#swagger.description = 'Upload a new file to AnythingLLM to be parsed and prepared for embedding.'
#swagger.requestBody = {
@ -68,9 +69,9 @@ function apiDocumentEndpoints(app) {
]
}
}
}
}
}
}
}
#swagger.responses[403] = {
schema: {
"$ref": "#/definitions/InvalidAPIKey"
@ -105,6 +106,7 @@ function apiDocumentEndpoints(app) {
`Document ${originalname} uploaded processed and successfully. It is now available in documents.`
);
await Telemetry.sendTelemetry("document_uploaded");
await EventLogs.logEvent("document_uploaded");
response.status(200).json({ success: true, error: null, documents });
} catch (e) {
console.log(e.message, e);
@ -117,7 +119,7 @@ function apiDocumentEndpoints(app) {
"/v1/document/upload-link",
[validApiKey],
async (request, response) => {
/*
/*
#swagger.tags = ['Documents']
#swagger.description = 'Upload a valid URL for AnythingLLM to scrape and prepare for embedding.'
#swagger.requestBody = {
@ -132,7 +134,7 @@ function apiDocumentEndpoints(app) {
"link": "https://useanything.com"
}
}
}
}
}
}
#swagger.responses[200] = {
@ -161,9 +163,9 @@ function apiDocumentEndpoints(app) {
]
}
}
}
}
}
}
}
#swagger.responses[403] = {
schema: {
"$ref": "#/definitions/InvalidAPIKey"
@ -197,6 +199,7 @@ function apiDocumentEndpoints(app) {
`Link ${link} uploaded processed and successfully. It is now available in documents.`
);
await Telemetry.sendTelemetry("document_uploaded");
await EventLogs.logEvent("document_uploaded");
response.status(200).json({ success: true, error: null, documents });
} catch (e) {
console.log(e.message, e);
@ -206,7 +209,7 @@ function apiDocumentEndpoints(app) {
);
app.get("/v1/documents", [validApiKey], async (_, response) => {
/*
/*
#swagger.tags = ['Documents']
#swagger.description = 'List of all locally-stored documents in instance'
#swagger.responses[200] = {
@ -231,9 +234,9 @@ function apiDocumentEndpoints(app) {
}
}
}
}
}
}
}
}
#swagger.responses[403] = {
schema: {
"$ref": "#/definitions/InvalidAPIKey"
@ -250,7 +253,7 @@ function apiDocumentEndpoints(app) {
});
app.get("/v1/document/:docName", [validApiKey], async (request, response) => {
/*
/*
#swagger.tags = ['Documents']
#swagger.description = 'Get a single document by its unique AnythingLLM document name'
#swagger.parameters['docName'] = {
@ -281,9 +284,9 @@ function apiDocumentEndpoints(app) {
}
}
}
}
}
}
}
}
#swagger.responses[403] = {
schema: {
"$ref": "#/definitions/InvalidAPIKey"
@ -308,7 +311,7 @@ function apiDocumentEndpoints(app) {
"/v1/document/accepted-file-types",
[validApiKey],
async (_, response) => {
/*
/*
#swagger.tags = ['Documents']
#swagger.description = 'Check available filetypes and MIMEs that can be uploaded.'
#swagger.responses[200] = {
@ -337,9 +340,9 @@ function apiDocumentEndpoints(app) {
}
}
}
}
}
}
}
}
#swagger.responses[403] = {
schema: {
"$ref": "#/definitions/InvalidAPIKey"

View File

@ -16,6 +16,7 @@ const {
writeResponseChunk,
VALID_CHAT_MODE,
} = require("../../../utils/chats/stream");
const { EventLogs } = require("../../../models/eventLogs");
function apiWorkspaceEndpoints(app) {
if (!app) return;
@ -73,6 +74,12 @@ function apiWorkspaceEndpoints(app) {
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
});
await EventLogs.logEvent("workspace_created", {
multiUserMode: multiUserMode(response),
LLMSelection: process.env.LLM_PROVIDER || "openai",
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
});
response.status(200).json({ workspace, message });
} catch (e) {
console.log(e.message, e);
@ -519,6 +526,11 @@ function apiWorkspaceEndpoints(app) {
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
});
await EventLogs.logEvent("sent_chat", {
LLMSelection: process.env.LLM_PROVIDER || "openai",
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
});
response.status(200).json({ ...result });
} catch (e) {
response.status(500).json({
@ -637,6 +649,11 @@ function apiWorkspaceEndpoints(app) {
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
});
await EventLogs.logEvent("sent_chat", {
LLMSelection: process.env.LLM_PROVIDER || "openai",
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
});
response.end();
} catch (e) {
console.error(e);

View File

@ -99,16 +99,17 @@ function chatEndpoints(app) {
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
});
await EventLogs.logEvent({
event: "sent_chat",
userId: user?.id || null,
metadata: {
await EventLogs.logEvent(
"sent_chat",
{
multiUserMode: multiUserMode(response),
LLMSelection: process.env.LLM_PROVIDER || "openai",
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
},
});
user?.id
);
response.end();
} catch (e) {
console.error(e);

View File

@ -1,3 +1,4 @@
const { EventLogs } = require("../../models/eventLogs");
const { Telemetry } = require("../../models/telemetry");
const {
forwardExtensionRequest,
@ -42,6 +43,9 @@ function extensionEndpoints(app) {
await Telemetry.sendTelemetry("extension_invoked", {
type: "github_repo",
});
await EventLogs.logEvent("extension_invoked", {
type: "github_repo",
});
response.status(200).json(responseFromProcessor);
} catch (e) {
console.error(e);
@ -63,6 +67,9 @@ function extensionEndpoints(app) {
await Telemetry.sendTelemetry("extension_invoked", {
type: "youtube_transcript",
});
await EventLogs.logEvent("extension_invoked", {
type: "youtube_transcript",
});
response.status(200).json(responseFromProcessor);
} catch (e) {
console.error(e);

View File

@ -150,11 +150,12 @@ function systemEndpoints(app) {
existingUser?.id
);
await EventLogs.logEvent({
event: "login_event",
userId: existingUser?.id || null,
metadata: {},
});
await EventLogs.logEvent(
"login_event",
{ multiUserMode: false },
existingUser?.id
);
response.status(200).json({
valid: true,
user: existingUser,
@ -182,6 +183,7 @@ function systemEndpoints(app) {
}
await Telemetry.sendTelemetry("login_event", { multiUserMode: false });
await EventLogs.logEvent("login_event", { multiUserMode: false });
response.status(200).json({
valid: true,
token: makeJWT({ p: password }, "30d"),
@ -371,6 +373,9 @@ function systemEndpoints(app) {
await Telemetry.sendTelemetry("enabled_multi_user_mode", {
multiUserMode: true,
});
await EventLogs.logEvent("enabled_multi_user_mode", {
multiUserMode: true,
});
response.status(200).json({ success: !!user, error });
} catch (e) {
await User.delete({});
@ -751,6 +756,26 @@ function systemEndpoints(app) {
}
);
app.post(
"/system/logs",
[validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],
async (request, response) => {
try {
const { offset = 0, limit = 20 } = reqBody(request);
const logs = await EventLogs.whereWithData({}, limit, offset * limit, {
id: "desc",
});
const totalLogs = await EventLogs.count();
const hasPages = totalLogs > (offset + 1) * limit;
response.status(200).json({ logs: logs, hasPages, totalLogs });
} catch (e) {
console.error(e);
response.sendStatus(500).end();
}
}
);
app.post(
"/system/workspace-chats",
[validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],

View File

@ -17,6 +17,7 @@ const {
flexUserRoleValid,
ROLES,
} = require("../utils/middleware/multiUserProtected");
const { EventLogs } = require("../models/eventLogs");
const { handleUploads } = setupMulter();
function workspaceEndpoints(app) {
@ -40,6 +41,17 @@ function workspaceEndpoints(app) {
},
user?.id
);
await EventLogs.logEvent(
"workspace_created",
{
multiUserMode: multiUserMode(response),
LLMSelection: process.env.LLM_PROVIDER || "openai",
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
},
user?.id
);
if (onboardingComplete === true)
await Telemetry.sendTelemetry("onboarding_complete");
@ -109,6 +121,7 @@ function workspaceEndpoints(app) {
`Document ${originalname} uploaded processed and successfully. It is now available in documents.`
);
await Telemetry.sendTelemetry("document_uploaded");
await EventLogs.logEvent("document_uploaded");
response.status(200).json({ success: true, error: null });
}
);
@ -141,6 +154,7 @@ function workspaceEndpoints(app) {
`Link ${link} uploaded processed and successfully. It is now available in documents.`
);
await Telemetry.sendTelemetry("link_uploaded");
await EventLogs.logEvent("link_uploaded");
response.status(200).json({ success: true, error: null });
}
);

View File

@ -1,5 +1,6 @@
const { Telemetry } = require("./telemetry");
const prisma = require("../utils/prisma");
const { EventLogs } = require("./eventLogs");
const ApiKey = {
tablename: "api_keys",
@ -20,6 +21,7 @@ const ApiKey = {
});
await Telemetry.sendTelemetry("api_key_created");
await EventLogs.logEvent("api_key_created");
return { apiKey, error: null };
} catch (error) {
console.error("FAILED TO CREATE API KEY.", error.message);

View File

@ -3,6 +3,7 @@ const { v4: uuidv4 } = require("uuid");
const { getVectorDbClass } = require("../utils/helpers");
const prisma = require("../utils/prisma");
const { Telemetry } = require("./telemetry");
const { EventLogs } = require("./eventLogs");
const Document = {
forWorkspace: async function (workspaceId = null) {
@ -84,6 +85,11 @@ const Document = {
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
});
await EventLogs.logEvent("documents_embedded_in_workspace", {
LLMSelection: process.env.LLM_PROVIDER || "openai",
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
});
return { failedToEmbed, errors: Array.from(errors), embedded };
},
@ -119,6 +125,11 @@ const Document = {
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
});
await EventLogs.logEvent("documents_removed_in_workspace", {
LLMSelection: process.env.LLM_PROVIDER || "openai",
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
});
return true;
},

View File

@ -1,22 +1,20 @@
const prisma = require("../utils/prisma");
const EventLogs = {
logEvent: async function ({
logEvent: async function (
event,
description = null,
metadata = null,
metadata = {},
userId = null,
ipAddress = null,
}) {
ipAddress = null
) {
try {
const eventLog = await prisma.event_logs.create({
data: {
event,
description,
metadata: metadata ? JSON.stringify(metadata) : null,
userId,
userId: userId ? Number(userId) : null,
ipAddress: ipAddress ? ipAddress : null,
occurredAt: new Date(),
ipAddress,
},
});
return { eventLog, message: null };
@ -80,6 +78,31 @@ const EventLogs = {
}
},
whereWithData: async function (
clause = {},
limit = null,
offset = null,
orderBy = null
) {
const { User } = require("./user");
try {
const results = await this.where(clause, limit, orderBy, offset);
for (const res of results) {
const user = res.userId ? await User.get({ id: res.userId }) : null;
res.user = user
? { username: user.username }
: { username: "unknown user" };
}
return results;
} catch (error) {
console.error(error.message);
return [];
}
},
count: async function (clause = {}) {
try {
const count = await prisma.event_logs.count({

View File

@ -2,11 +2,9 @@
CREATE TABLE "event_logs" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"event" TEXT NOT NULL,
"description" TEXT,
"metadata" TEXT,
"userId" INTEGER,
"occurredAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"ipAddress" TEXT
"occurredAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- CreateIndex

View File

@ -135,11 +135,9 @@ model cache_data {
model event_logs {
id Int @id @default(autoincrement())
event String
description String?
metadata String?
userId Int?
occurredAt DateTime @default(now())
ipAddress String?
@@index([userId])
@@index([event])
}