vectra-js 0.9.2 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/.github/dependabot.yml +11 -0
- package/.github/workflows/npm-publish.yml +39 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/README.md +30 -0
- package/bin/vectra.js +9 -3
- package/package.json +24 -22
- package/src/backends/chroma_store.js +13 -2
- package/src/config.js +9 -0
- package/src/core.js +203 -1
- package/src/dashboard/dashboard-script.js +260 -0
- package/src/dashboard/index.html +362 -0
- package/src/dashboard/logo.png +0 -0
- package/src/dashboard/trace-script.js +184 -0
- package/src/dashboard/trace.html +239 -0
- package/src/observability.js +226 -0
- package/src/processor.js +1 -1
- package/src/ui/index.html +278 -236
- package/src/ui/logo.png +0 -0
- package/src/ui/script.js +59 -10
- package/src/ui/style.css +2 -2
- package/src/webconfig_server.js +162 -2
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Trace Details - Vectra Dashboard</title>
|
|
7
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
|
+
<script>
|
|
9
|
+
tailwind.config = {
|
|
10
|
+
darkMode: 'class',
|
|
11
|
+
theme: {
|
|
12
|
+
extend: {
|
|
13
|
+
colors: {
|
|
14
|
+
brand: {
|
|
15
|
+
50: '#f5f3ff',
|
|
16
|
+
100: '#ede9fe',
|
|
17
|
+
200: '#ddd6fe',
|
|
18
|
+
300: '#c4b5fd',
|
|
19
|
+
400: '#a78bfa',
|
|
20
|
+
500: '#8b5cf6',
|
|
21
|
+
600: '#7c3aed',
|
|
22
|
+
700: '#6d28d9',
|
|
23
|
+
800: '#5b21b6',
|
|
24
|
+
900: '#4c1d95',
|
|
25
|
+
950: '#2e1065',
|
|
26
|
+
},
|
|
27
|
+
dark: {
|
|
28
|
+
950: '#020204',
|
|
29
|
+
900: '#08080C',
|
|
30
|
+
850: '#0F0F16',
|
|
31
|
+
800: '#14141F',
|
|
32
|
+
700: '#1C1C2E',
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Check for saved theme preference or use system preference
|
|
40
|
+
if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
|
41
|
+
document.documentElement.classList.add('dark');
|
|
42
|
+
} else {
|
|
43
|
+
document.documentElement.classList.remove('dark');
|
|
44
|
+
}
|
|
45
|
+
</script>
|
|
46
|
+
<script src="https://unpkg.com/lucide@latest"></script>
|
|
47
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
|
48
|
+
<style>
|
|
49
|
+
body { font-family: 'Inter', sans-serif; }
|
|
50
|
+
.sidebar-link.active { background-color: #f3f4f6; color: #111827; }
|
|
51
|
+
.dark .sidebar-link.active { background-color: rgba(255, 255, 255, 0.1); color: #ffffff; }
|
|
52
|
+
.sidebar-link:hover { background-color: #f9fafb; color: #111827; }
|
|
53
|
+
.dark .sidebar-link:hover { background-color: rgba(255, 255, 255, 0.05); color: #ffffff; }
|
|
54
|
+
|
|
55
|
+
/* JSON Syntax Highlighting */
|
|
56
|
+
.json-key { color: #7c3aed; } /* violet-600 */
|
|
57
|
+
.dark .json-key { color: #a78bfa; } /* violet-400 */
|
|
58
|
+
.json-string { color: #059669; } /* emerald-600 */
|
|
59
|
+
.dark .json-string { color: #34d399; } /* emerald-400 */
|
|
60
|
+
.json-number { color: #d97706; } /* amber-600 */
|
|
61
|
+
.dark .json-number { color: #fbbf24; } /* amber-400 */
|
|
62
|
+
.json-boolean { color: #dc2626; } /* red-600 */
|
|
63
|
+
.dark .json-boolean { color: #f87171; } /* red-400 */
|
|
64
|
+
.json-null { color: #9ca3af; } /* gray-400 */
|
|
65
|
+
.dark .json-null { color: #9ca3af; } /* gray-400 */
|
|
66
|
+
|
|
67
|
+
/* Custom scrollbar for dark mode compatibility */
|
|
68
|
+
::-webkit-scrollbar {
|
|
69
|
+
width: 8px;
|
|
70
|
+
height: 8px;
|
|
71
|
+
}
|
|
72
|
+
::-webkit-scrollbar-track {
|
|
73
|
+
background: transparent;
|
|
74
|
+
}
|
|
75
|
+
::-webkit-scrollbar-thumb {
|
|
76
|
+
background: #cbd5e1;
|
|
77
|
+
border-radius: 4px;
|
|
78
|
+
}
|
|
79
|
+
.dark ::-webkit-scrollbar-thumb {
|
|
80
|
+
background: #334155;
|
|
81
|
+
}
|
|
82
|
+
::-webkit-scrollbar-thumb:hover {
|
|
83
|
+
background: #94a3b8;
|
|
84
|
+
}
|
|
85
|
+
.dark ::-webkit-scrollbar-thumb:hover {
|
|
86
|
+
background: #475569;
|
|
87
|
+
}
|
|
88
|
+
</style>
|
|
89
|
+
</head>
|
|
90
|
+
<body class="bg-gray-50 text-slate-800 dark:bg-dark-950 dark:text-white h-screen overflow-hidden flex transition-colors duration-200">
|
|
91
|
+
|
|
92
|
+
<!-- Sidebar -->
|
|
93
|
+
<aside class="w-64 bg-white border-r border-gray-200 dark:bg-dark-900 dark:border-white/5 flex-col hidden md:flex transition-colors duration-200">
|
|
94
|
+
<!-- Logo -->
|
|
95
|
+
<div class="h-16 flex items-center px-6 border-b border-gray-100 dark:border-white/5">
|
|
96
|
+
<div class="flex items-center gap-2">
|
|
97
|
+
<div class="bg-brand-600 p-1.5 rounded-lg text-white">
|
|
98
|
+
<i data-lucide="layers" class="w-5 h-5"></i>
|
|
99
|
+
</div>
|
|
100
|
+
<span class="text-xl font-bold text-slate-900 dark:text-white tracking-tight">Vectra</span>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<!-- Navigation -->
|
|
105
|
+
<nav class="flex-1 px-4 py-6 space-y-1 overflow-y-auto">
|
|
106
|
+
<div class="mb-6">
|
|
107
|
+
<p class="px-2 text-xs font-semibold text-gray-400 dark:text-gray-500 uppercase tracking-wider mb-2">Menu</p>
|
|
108
|
+
<a href="/dashboard/" class="w-full flex items-center px-3 py-2 text-sm font-medium rounded-lg text-gray-600 dark:text-gray-400 sidebar-link group transition-colors">
|
|
109
|
+
<i data-lucide="layout-dashboard" class="w-5 h-5 mr-3 text-gray-400 dark:text-gray-500 group-hover:text-gray-500 dark:group-hover:text-gray-300"></i>
|
|
110
|
+
Dashboard
|
|
111
|
+
</a>
|
|
112
|
+
<a href="/dashboard/?view=traces" class="w-full flex items-center px-3 py-2 text-sm font-medium rounded-lg text-slate-900 dark:text-white bg-gray-100 dark:bg-white/10 sidebar-link active group transition-colors">
|
|
113
|
+
<i data-lucide="activity" class="w-5 h-5 mr-3 text-gray-500 dark:text-gray-400"></i>
|
|
114
|
+
Traces
|
|
115
|
+
</a>
|
|
116
|
+
<a href="/dashboard/?view=sessions" class="w-full flex items-center px-3 py-2 text-sm font-medium rounded-lg text-gray-600 dark:text-gray-400 sidebar-link group transition-colors">
|
|
117
|
+
<i data-lucide="users" class="w-5 h-5 mr-3 text-gray-400 dark:text-gray-500 group-hover:text-gray-500 dark:group-hover:text-gray-300"></i>
|
|
118
|
+
Sessions
|
|
119
|
+
</a>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div>
|
|
123
|
+
<p class="px-2 text-xs font-semibold text-gray-400 dark:text-gray-500 uppercase tracking-wider mb-2">Management</p>
|
|
124
|
+
<a href="#" class="w-full flex items-center px-3 py-2 text-sm font-medium rounded-lg text-gray-600 dark:text-gray-400 sidebar-link group transition-colors">
|
|
125
|
+
<i data-lucide="settings" class="w-5 h-5 mr-3 text-gray-400 dark:text-gray-500 group-hover:text-gray-500 dark:group-hover:text-gray-300"></i>
|
|
126
|
+
Settings
|
|
127
|
+
</a>
|
|
128
|
+
<a href="#" class="w-full flex items-center px-3 py-2 text-sm font-medium rounded-lg text-gray-600 dark:text-gray-400 sidebar-link group transition-colors">
|
|
129
|
+
<i data-lucide="book-open" class="w-5 h-5 mr-3 text-gray-400 dark:text-gray-500 group-hover:text-gray-500 dark:group-hover:text-gray-300"></i>
|
|
130
|
+
Documentation
|
|
131
|
+
</a>
|
|
132
|
+
</div>
|
|
133
|
+
</nav>
|
|
134
|
+
|
|
135
|
+
<!-- User Profile (Bottom) -->
|
|
136
|
+
<div class="p-4 border-t border-gray-100 dark:border-white/5">
|
|
137
|
+
<div class="flex items-center gap-3">
|
|
138
|
+
<div class="w-8 h-8 rounded-full bg-brand-100 dark:bg-brand-900/50 flex items-center justify-center text-brand-600 dark:text-brand-400 font-bold text-xs border border-brand-200 dark:border-brand-500/20">
|
|
139
|
+
AD
|
|
140
|
+
</div>
|
|
141
|
+
<div>
|
|
142
|
+
<p class="text-sm font-medium text-gray-900 dark:text-white">Admin User</p>
|
|
143
|
+
<p class="text-xs text-gray-500">admin@vectra.ai</p>
|
|
144
|
+
</div>
|
|
145
|
+
<button onclick="toggleTheme()" class="ml-auto p-1.5 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-white/5 transition-colors">
|
|
146
|
+
<i data-lucide="moon" class="w-4 h-4 hidden dark:block"></i>
|
|
147
|
+
<i data-lucide="sun" class="w-4 h-4 block dark:hidden"></i>
|
|
148
|
+
</button>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
</aside>
|
|
152
|
+
|
|
153
|
+
<!-- Main Content -->
|
|
154
|
+
<div class="flex-1 flex flex-col min-w-0 overflow-hidden bg-gray-50 dark:bg-dark-950 transition-colors duration-200">
|
|
155
|
+
|
|
156
|
+
<!-- Top Header -->
|
|
157
|
+
<header class="bg-white dark:bg-dark-900 border-b border-gray-200 dark:border-white/5 h-16 flex items-center justify-between px-6 lg:px-8 transition-colors duration-200">
|
|
158
|
+
<div class="flex items-center flex-1">
|
|
159
|
+
<div class="flex items-center text-sm text-gray-500 dark:text-gray-400">
|
|
160
|
+
<a href="/dashboard/" class="hover:text-gray-900 dark:hover:text-white transition-colors">Traces</a>
|
|
161
|
+
<i data-lucide="chevron-right" class="w-4 h-4 mx-2"></i>
|
|
162
|
+
<span class="font-medium text-gray-900 dark:text-white" id="header-trace-id">Loading...</span>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
<div class="ml-4 flex items-center gap-4">
|
|
166
|
+
<button class="p-2 text-gray-400 hover:text-gray-500 dark:hover:text-white transition-colors">
|
|
167
|
+
<i data-lucide="bell" class="w-6 h-6"></i>
|
|
168
|
+
</button>
|
|
169
|
+
</div>
|
|
170
|
+
</header>
|
|
171
|
+
|
|
172
|
+
<!-- Main Scrollable Area -->
|
|
173
|
+
<main class="flex-1 overflow-y-auto p-6 lg:p-8" id="main-content">
|
|
174
|
+
|
|
175
|
+
<!-- Loading State -->
|
|
176
|
+
<div id="loading" class="flex flex-col justify-center items-center h-64">
|
|
177
|
+
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-brand-600 dark:border-brand-500 mb-4"></div>
|
|
178
|
+
<p class="text-sm text-gray-500 dark:text-gray-400">Loading trace details...</p>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
<!-- Trace Content (Hidden initially) -->
|
|
182
|
+
<div id="trace-content" class="hidden space-y-8 max-w-7xl mx-auto">
|
|
183
|
+
|
|
184
|
+
<!-- Header Card -->
|
|
185
|
+
<div class="bg-white dark:bg-dark-900 rounded-xl shadow-sm border border-gray-100 dark:border-white/5 p-6 transition-colors duration-200">
|
|
186
|
+
<div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-6 gap-4">
|
|
187
|
+
<div>
|
|
188
|
+
<h1 class="text-2xl font-bold text-gray-900 dark:text-white mb-1">Trace Summary</h1>
|
|
189
|
+
<div class="flex items-center space-x-6 text-sm text-gray-500 dark:text-gray-400">
|
|
190
|
+
<span class="flex items-center"><i data-lucide="calendar" class="w-4 h-4 mr-2 text-gray-400 dark:text-gray-500"></i> <span id="trace-timestamp">-</span></span>
|
|
191
|
+
<span class="flex items-center"><i data-lucide="clock" class="w-4 h-4 mr-2 text-gray-400 dark:text-gray-500"></i> <span id="trace-duration">-</span></span>
|
|
192
|
+
<span class="flex items-center"><i data-lucide="cpu" class="w-4 h-4 mr-2 text-gray-400 dark:text-gray-500"></i> <span id="trace-model">-</span></span>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
<div id="trace-status" class="px-4 py-1.5 rounded-full text-sm font-semibold bg-gray-100 text-gray-600 border border-gray-200 dark:bg-white/10 dark:text-gray-300 dark:border-white/10">
|
|
196
|
+
Unknown
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
<!-- Timeline / Gantt -->
|
|
201
|
+
<div class="border-t border-gray-100 dark:border-white/5 pt-6">
|
|
202
|
+
<h3 class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-4">Execution Timeline</h3>
|
|
203
|
+
<div id="timeline-container" class="space-y-3 relative">
|
|
204
|
+
<!-- Spans injected here -->
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
<!-- Detailed Spans List -->
|
|
210
|
+
<div class="space-y-6">
|
|
211
|
+
<div class="flex items-center justify-between">
|
|
212
|
+
<h3 class="text-lg font-bold text-gray-900 dark:text-white">Span Details</h3>
|
|
213
|
+
<div class="text-sm text-gray-500 dark:text-gray-400">Detailed breakdown of operations</div>
|
|
214
|
+
</div>
|
|
215
|
+
<div id="spans-list" class="space-y-6">
|
|
216
|
+
<!-- Span Cards injected here -->
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
</div>
|
|
221
|
+
</main>
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
<script src="trace-script.js"></script>
|
|
225
|
+
<script>
|
|
226
|
+
lucide.createIcons();
|
|
227
|
+
|
|
228
|
+
function toggleTheme() {
|
|
229
|
+
if (document.documentElement.classList.contains('dark')) {
|
|
230
|
+
document.documentElement.classList.remove('dark');
|
|
231
|
+
localStorage.setItem('color-theme', 'light');
|
|
232
|
+
} else {
|
|
233
|
+
document.documentElement.classList.add('dark');
|
|
234
|
+
localStorage.setItem('color-theme', 'dark');
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
</script>
|
|
238
|
+
</body>
|
|
239
|
+
</html>
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
const { v4: uuidv4 } = require('uuid');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
class SQLiteLogger {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
this.enabled = config.enabled;
|
|
7
|
+
if (!this.enabled) return;
|
|
8
|
+
|
|
9
|
+
this.projectId = config.projectId;
|
|
10
|
+
this.trackMetrics = config.trackMetrics;
|
|
11
|
+
this.trackTraces = config.trackTraces;
|
|
12
|
+
this.trackLogs = config.trackLogs;
|
|
13
|
+
this.sessionTracking = config.sessionTracking;
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
const rawPath = config.sqlitePath || 'vectra-observability.db';
|
|
17
|
+
const dbPath = path.isAbsolute(rawPath) ? rawPath : path.resolve(process.cwd(), rawPath);
|
|
18
|
+
// Ensure directory exists
|
|
19
|
+
const dbDir = path.dirname(dbPath);
|
|
20
|
+
console.log(`[SQLiteLogger] dbPath: ${dbPath}, dbDir: ${dbDir}`);
|
|
21
|
+
|
|
22
|
+
const fs = require('fs');
|
|
23
|
+
if (!fs.existsSync(dbDir)) {
|
|
24
|
+
console.log(`[SQLiteLogger] Creating directory: ${dbDir}`);
|
|
25
|
+
fs.mkdirSync(dbDir, { recursive: true });
|
|
26
|
+
} else {
|
|
27
|
+
console.log(`[SQLiteLogger] Directory exists: ${dbDir}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const sqlite3 = require('sqlite3').verbose();
|
|
31
|
+
this.db = new sqlite3.Database(dbPath, (err) => {
|
|
32
|
+
if (err) {
|
|
33
|
+
console.error('Failed to connect to SQLite database:', err);
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
this.initializeSchema();
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error('Failed to initialize SQLite logger:', error);
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
initializeSchema() {
|
|
45
|
+
this.db.serialize(() => {
|
|
46
|
+
this.db.run(`
|
|
47
|
+
CREATE TABLE IF NOT EXISTS traces (
|
|
48
|
+
id TEXT PRIMARY KEY,
|
|
49
|
+
project_id TEXT,
|
|
50
|
+
trace_id TEXT,
|
|
51
|
+
span_id TEXT,
|
|
52
|
+
parent_span_id TEXT,
|
|
53
|
+
name TEXT,
|
|
54
|
+
start_time INTEGER,
|
|
55
|
+
end_time INTEGER,
|
|
56
|
+
duration INTEGER,
|
|
57
|
+
status TEXT,
|
|
58
|
+
attributes TEXT, -- JSON
|
|
59
|
+
input TEXT, -- JSON
|
|
60
|
+
output TEXT, -- JSON
|
|
61
|
+
error TEXT, -- JSON
|
|
62
|
+
provider TEXT,
|
|
63
|
+
model_name TEXT
|
|
64
|
+
)
|
|
65
|
+
`);
|
|
66
|
+
|
|
67
|
+
// Attempt to add columns if they don't exist (migration)
|
|
68
|
+
this.db.run(`ALTER TABLE traces ADD COLUMN provider TEXT`, () => {});
|
|
69
|
+
this.db.run(`ALTER TABLE traces ADD COLUMN model_name TEXT`, () => {});
|
|
70
|
+
|
|
71
|
+
this.db.run(`
|
|
72
|
+
CREATE TABLE IF NOT EXISTS metrics (
|
|
73
|
+
id TEXT PRIMARY KEY,
|
|
74
|
+
project_id TEXT,
|
|
75
|
+
name TEXT,
|
|
76
|
+
value REAL,
|
|
77
|
+
timestamp INTEGER,
|
|
78
|
+
tags TEXT -- JSON
|
|
79
|
+
)
|
|
80
|
+
`);
|
|
81
|
+
|
|
82
|
+
this.db.run(`
|
|
83
|
+
CREATE TABLE IF NOT EXISTS logs (
|
|
84
|
+
id TEXT PRIMARY KEY,
|
|
85
|
+
project_id TEXT,
|
|
86
|
+
level TEXT,
|
|
87
|
+
message TEXT,
|
|
88
|
+
timestamp INTEGER,
|
|
89
|
+
context TEXT -- JSON
|
|
90
|
+
)
|
|
91
|
+
`);
|
|
92
|
+
|
|
93
|
+
this.db.run(`
|
|
94
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
95
|
+
id TEXT PRIMARY KEY,
|
|
96
|
+
project_id TEXT,
|
|
97
|
+
session_id TEXT,
|
|
98
|
+
user_id TEXT,
|
|
99
|
+
start_time INTEGER,
|
|
100
|
+
last_activity_time INTEGER,
|
|
101
|
+
metadata TEXT -- JSON
|
|
102
|
+
)
|
|
103
|
+
`);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
logTrace(trace) {
|
|
108
|
+
if (!this.enabled || !this.trackTraces) return;
|
|
109
|
+
try {
|
|
110
|
+
const stmt = this.db.prepare(`
|
|
111
|
+
INSERT INTO traces (id, project_id, trace_id, span_id, parent_span_id, name, start_time, end_time, duration, status, attributes, input, output, error, provider, model_name)
|
|
112
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
113
|
+
`);
|
|
114
|
+
stmt.run(
|
|
115
|
+
uuidv4(),
|
|
116
|
+
this.projectId,
|
|
117
|
+
trace.traceId,
|
|
118
|
+
trace.spanId,
|
|
119
|
+
trace.parentSpanId || null,
|
|
120
|
+
trace.name,
|
|
121
|
+
trace.startTime,
|
|
122
|
+
trace.endTime,
|
|
123
|
+
trace.duration,
|
|
124
|
+
trace.status,
|
|
125
|
+
JSON.stringify(trace.attributes || {}),
|
|
126
|
+
JSON.stringify(trace.input || {}),
|
|
127
|
+
JSON.stringify(trace.output || {}),
|
|
128
|
+
JSON.stringify(trace.error || {}),
|
|
129
|
+
trace.provider || null,
|
|
130
|
+
trace.modelName || null
|
|
131
|
+
);
|
|
132
|
+
stmt.finalize();
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error('Failed to log trace:', error);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
logMetric(nameOrObj, value, tags = {}) {
|
|
139
|
+
if (!this.enabled || !this.trackMetrics) return;
|
|
140
|
+
|
|
141
|
+
let metricName = nameOrObj;
|
|
142
|
+
let metricValue = value;
|
|
143
|
+
let metricTags = tags;
|
|
144
|
+
|
|
145
|
+
if (typeof nameOrObj === 'object' && nameOrObj !== null) {
|
|
146
|
+
metricName = nameOrObj.name;
|
|
147
|
+
metricValue = nameOrObj.value;
|
|
148
|
+
metricTags = nameOrObj.tags || {};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
const stmt = this.db.prepare(`
|
|
153
|
+
INSERT INTO metrics (id, project_id, name, value, timestamp, tags)
|
|
154
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
155
|
+
`);
|
|
156
|
+
stmt.run(
|
|
157
|
+
uuidv4(),
|
|
158
|
+
this.projectId,
|
|
159
|
+
metricName,
|
|
160
|
+
metricValue,
|
|
161
|
+
Date.now(),
|
|
162
|
+
JSON.stringify(metricTags)
|
|
163
|
+
);
|
|
164
|
+
stmt.finalize();
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.error('Failed to log metric:', error);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
log(level, message, context = {}) {
|
|
171
|
+
if (!this.enabled || !this.trackLogs) return;
|
|
172
|
+
try {
|
|
173
|
+
const stmt = this.db.prepare(`
|
|
174
|
+
INSERT INTO logs (id, project_id, level, message, timestamp, context)
|
|
175
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
176
|
+
`);
|
|
177
|
+
stmt.run(
|
|
178
|
+
uuidv4(),
|
|
179
|
+
this.projectId,
|
|
180
|
+
level,
|
|
181
|
+
message,
|
|
182
|
+
Date.now(),
|
|
183
|
+
JSON.stringify(context)
|
|
184
|
+
);
|
|
185
|
+
stmt.finalize();
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error('Failed to log message:', error);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
logSession(sessionId, userId, metadata = {}) {
|
|
192
|
+
if (!this.enabled || !this.sessionTracking) return;
|
|
193
|
+
try {
|
|
194
|
+
// Check if session exists (upsert logic if needed, but here simple insert/update)
|
|
195
|
+
// For simplicity, we just insert or ignore, or update last_activity
|
|
196
|
+
// Since sqlite3 doesn't support UPSERT in older versions easily without ON CONFLICT, let's try INSERT OR REPLACE
|
|
197
|
+
const stmt = this.db.prepare(`
|
|
198
|
+
INSERT OR REPLACE INTO sessions (id, project_id, session_id, user_id, start_time, last_activity_time, metadata)
|
|
199
|
+
VALUES (
|
|
200
|
+
COALESCE((SELECT id FROM sessions WHERE session_id = ?), ?),
|
|
201
|
+
?, ?, ?,
|
|
202
|
+
COALESCE((SELECT start_time FROM sessions WHERE session_id = ?), ?),
|
|
203
|
+
?, ?
|
|
204
|
+
)
|
|
205
|
+
`);
|
|
206
|
+
|
|
207
|
+
const now = Date.now();
|
|
208
|
+
const newId = uuidv4();
|
|
209
|
+
|
|
210
|
+
stmt.run(
|
|
211
|
+
sessionId, newId,
|
|
212
|
+
this.projectId,
|
|
213
|
+
sessionId,
|
|
214
|
+
userId,
|
|
215
|
+
sessionId, now,
|
|
216
|
+
now,
|
|
217
|
+
JSON.stringify(metadata)
|
|
218
|
+
);
|
|
219
|
+
stmt.finalize();
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.error('Failed to log session:', error);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
module.exports = SQLiteLogger;
|
package/src/processor.js
CHANGED