Logs.constants.ts788 lines · main
1import { IS_PLATFORM } from 'common'
2import dayjs from 'dayjs'
3
4import type { DatetimeHelper, FilterTableSet, LogTemplate } from './Logs.types'
5import { DOCS_URL } from '@/lib/constants'
6
7export const LOGS_EXPLORER_DOCS_URL = `${DOCS_URL}/guides/platform/logs#querying-with-the-logs-explorer`
8
9export const LOGS_LARGE_DATE_RANGE_DAYS_THRESHOLD = 2 // IN DAYS
10
11export const TEMPLATES: LogTemplate[] = [
12 {
13 label: 'Recent Errors',
14 mode: 'simple',
15 searchString: '[Ee]rror|\\s[45][0-9][0-9]\\s',
16 for: ['api'],
17 },
18 {
19 label: 'Commits',
20 mode: 'simple',
21 searchString: 'COMMIT',
22 for: ['database'],
23 },
24 {
25 label: 'Commits By User',
26 description: 'Count of commits made by users on the database',
27 mode: 'custom',
28 searchString: `select
29 p.user_name,
30 count(*) as count
31from postgres_logs
32 left join unnest(metadata) as m on true
33 left join unnest(m.parsed) as p on true
34where
35 regexp_contains(event_message, 'COMMIT')
36group by
37 p.user_name
38 `,
39 for: ['database'],
40 },
41 {
42 label: 'Metadata IP',
43 description: 'List all IP addresses that used the Briven API',
44 mode: 'custom',
45 searchString: `select
46 cast(timestamp as datetime) as timestamp,
47 h.x_real_ip
48from edge_logs
49 left join unnest(metadata) as m on true
50 left join unnest(m.request) as r on true
51 left join unnest(r.headers) as h on true
52where h.x_real_ip is not null
53`,
54 for: ['api'],
55 },
56 {
57 label: 'Requests by Geography',
58 description: 'List all ISO 3166-1 alpha-2 country codes that used the Briven API',
59 mode: 'custom',
60 searchString: `select
61 cf.country,
62 count(*) as count
63from edge_logs
64 left join unnest(metadata) as m on true
65 left join unnest(m.request) as r on true
66 left join unnest(r.cf) as cf on true
67group by
68 cf.country
69order by
70 count desc
71`,
72 for: ['api'],
73 },
74 {
75 label: 'Slow Response Time',
76 mode: 'custom',
77 description: 'List all Briven API requests that are slow',
78 searchString: `select
79 cast(timestamp as datetime) as timestamp,
80 event_message,
81 r.origin_time
82from edge_logs
83 cross join unnest(metadata) as m
84 cross join unnest(m.response) as r
85where
86 r.origin_time > 1000
87order by
88 timestamp desc
89limit 100
90`,
91 for: ['api'],
92 },
93 {
94 label: '500 Request Codes',
95 description: 'List all Briven API requests that responded witha 5XX status code',
96 mode: 'custom',
97 searchString: `select
98 cast(timestamp as datetime) as timestamp,
99 event_message,
100 r.status_code
101from edge_logs
102 cross join unnest(metadata) as m
103 cross join unnest(m.response) as r
104where
105 r.status_code >= 500
106order by
107 timestamp desc
108limit 100
109`,
110 for: ['api'],
111 },
112 {
113 label: 'Top Paths',
114 description: 'List the most requested Briven API paths',
115 mode: 'custom',
116 searchString: `select
117 r.path as path,
118 r.search as params,
119 count(timestamp) as c
120from edge_logs
121 cross join unnest(metadata) as m
122 cross join unnest(m.request) as r
123group by
124 path,
125 params
126order by
127 c desc
128limit 100
129`,
130 for: ['api'],
131 },
132 {
133 label: 'REST Requests',
134 description: 'List all PostgREST requests',
135 mode: 'custom',
136 searchString: `select
137 cast(timestamp as datetime) as timestamp,
138 event_message
139from edge_logs
140 cross join unnest(metadata) as m
141 cross join unnest(m.request) as r
142where
143 path like '%rest/v1%'
144order by
145 timestamp desc
146limit 100
147`,
148 for: ['api'],
149 },
150 {
151 label: 'Errors',
152 description: 'List all Postgres error messages with ERROR, FATAL, or PANIC severity',
153 mode: 'custom',
154 searchString: `select
155 cast(t.timestamp as datetime) as timestamp,
156 p.error_severity,
157 event_message
158from postgres_logs as t
159 cross join unnest(metadata) as m
160 cross join unnest(m.parsed) as p
161where
162 p.error_severity in ('ERROR', 'FATAL', 'PANIC')
163order by
164 timestamp desc
165limit 100
166`,
167 for: ['database'],
168 },
169 {
170 label: 'Error Count by User',
171 description: 'Count of errors by users',
172 mode: 'custom',
173 searchString: `select
174 count(t.timestamp) as count,
175 p.user_name,
176 p.error_severity
177from postgres_logs as t
178 cross join unnest(metadata) as m
179 cross join unnest(m.parsed) as p
180where
181 p.error_severity in ('ERROR', 'FATAL', 'PANIC')
182group by
183 p.user_name,
184 p.error_severity
185order by
186 count desc
187limit 100
188`,
189 for: ['database'],
190 },
191 {
192 label: 'Auth Endpoint Events',
193 description: 'Endpoint events filtered by path',
194 mode: 'custom',
195 searchString: `select
196 t.timestamp,
197 event_message
198from auth_logs as t
199where
200 regexp_contains(event_message,"level.{3}(info|warning||error|fatal)")
201 -- and regexp_contains(event_message,"path.{3}(/token|/recover|/signup|/otp)")
202limit 100
203`,
204 for: ['database'],
205 },
206 {
207 label: 'Auth Audit Logs',
208 description: 'Audit logs for auth events',
209 mode: 'custom',
210 searchString: `select
211 cast(timestamp as datetime) as timestamp,
212 event_message, metadata
213from auth_audit_logs
214limit 10
215`,
216 for: ['database'],
217 },
218 {
219 label: 'Storage Object Requests',
220 description: 'Number of requests done on Storage Objects',
221 mode: 'custom',
222 searchString: `select
223 r.method as http_verb,
224 r.path as filepath,
225 count(*) as num_requests
226from edge_logs
227 cross join unnest(metadata) as m
228 cross join unnest(m.request) AS r
229 cross join unnest(r.headers) AS h
230where
231 path like '%storage/v1/object/%'
232group by
233 r.path, r.method
234order by
235 num_requests desc
236limit 100
237`,
238 for: ['api'],
239 },
240 {
241 label: 'Storage Egress Requests',
242 description: 'Check the number of requests done on Storage Affecting Egress',
243 mode: 'custom',
244 searchString: `select
245 request.method as http_verb,
246 request.path as filepath,
247 (responseHeaders.cf_cache_status = 'HIT') as cached,
248 count(*) as num_requests
249from
250 edge_logs
251 cross join unnest(metadata) as metadata
252 cross join unnest(metadata.request) as request
253 cross join unnest(metadata.response) as response
254 cross join unnest(response.headers) as responseHeaders
255where
256 (path like '%storage/v1/object/%' or path like '%storage/v1/render/%')
257 and request.method = 'GET'
258group by 1, 2, 3
259order by num_requests desc
260limit 100;
261`,
262 for: ['api'],
263 },
264 {
265 label: 'Storage Top Cache Misses',
266 description: 'The top Storage requests that miss caching',
267 mode: 'custom',
268 searchString: `select
269 r.path as path,
270 r.search as search,
271 count(id) as count
272from edge_logs f
273 cross join unnest(f.metadata) as m
274 cross join unnest(m.request) as r
275 cross join unnest(m.response) as res
276 cross join unnest(res.headers) as h
277where starts_with(r.path, '/storage/v1/object')
278 and r.method = 'GET'
279 and h.cf_cache_status in ('MISS', 'NONE/UNKNOWN', 'EXPIRED', 'BYPASS', 'DYNAMIC')
280group by path, search
281order by count desc
282limit 100;
283`,
284 for: ['api'],
285 },
286]
287
288const _SQL_FILTER_COMMON = {
289 search_query: (value: string) => `regexp_contains(event_message, '${value}')`,
290}
291
292export const SQL_FILTER_TEMPLATES: any = {
293 postgres_logs: {
294 ..._SQL_FILTER_COMMON,
295 database: (value: string) => `identifier = '${value}'`,
296 'severity.error': `parsed.error_severity in ('ERROR', 'FATAL', 'PANIC')`,
297 'severity.noError': `parsed.error_severity not in ('ERROR', 'FATAL', 'PANIC')`,
298 'severity.log': `parsed.error_severity = 'LOG'`,
299 },
300 edge_logs: {
301 ..._SQL_FILTER_COMMON,
302 database: (value: string) => `identifier = '${value}'`,
303 'status_code.error': `response.status_code between 500 and 599`,
304 'status_code.success': `response.status_code between 200 and 299`,
305 'status_code.warning': `response.status_code between 400 and 499`,
306
307 'product.database': `request.path like '/rest/%' or request.path like '/graphql/%'`,
308 'product.storage': `request.path like '/storage/%'`,
309 'product.auth': `request.path like '/auth/%'`,
310 'product.realtime': `request.path like '/realtime/%'`,
311
312 'method.get': `request.method = 'GET'`,
313 'method.post': `request.method = 'POST'`,
314 'method.put': `request.method = 'PUT'`,
315 'method.patch': `request.method = 'PATCH'`,
316 'method.delete': `request.method = 'DELETE'`,
317 'method.options': `request.method = 'OPTIONS'`,
318 },
319 function_edge_logs: {
320 ..._SQL_FILTER_COMMON,
321 'status_code.error': `response.status_code between 500 and 599`,
322 'status_code.success': `response.status_code between 200 and 299`,
323 'status_code.warning': `response.status_code between 400 and 499`,
324 },
325 function_logs: {
326 ..._SQL_FILTER_COMMON,
327 'severity.error': `metadata.level = 'error'`,
328 'severity.notError': `metadata.level != 'error'`,
329 'severity.log': `metadata.level = 'log'`,
330 'severity.info': `metadata.level = 'info'`,
331 'severity.debug': `metadata.level = 'debug'`,
332 'severity.warn': `metadata.level = 'warn'`,
333 },
334 auth_logs: {
335 ..._SQL_FILTER_COMMON,
336 'severity.error': `metadata.level = 'error' or metadata.level = 'fatal'`,
337 'severity.warning': `metadata.level = 'warning'`,
338 'severity.info': `metadata.level = 'info'`,
339 'status_code.server_error': `cast(metadata.status as int64) between 500 and 599`,
340 'status_code.client_error': `cast(metadata.status as int64) between 400 and 499`,
341 'status_code.redirection': `cast(metadata.status as int64) between 300 and 399`,
342 'status_code.success': `cast(metadata.status as int64) between 200 and 299`,
343 'endpoints.admin': `REGEXP_CONTAINS(metadata.path, "/admin")`,
344 'endpoints.signup': `REGEXP_CONTAINS(metadata.path, "/signup|/invite|/verify")`,
345 'endpoints.authentication': `REGEXP_CONTAINS(metadata.path, "/token|/authorize|/callback|/otp|/magiclink")`,
346 'endpoints.recover': `REGEXP_CONTAINS(metadata.path, "/recover")`,
347 'endpoints.user': `REGEXP_CONTAINS(metadata.path, "/user")`,
348 'endpoints.logout': `REGEXP_CONTAINS(metadata.path, "/logout")`,
349 },
350 realtime_logs: {
351 ..._SQL_FILTER_COMMON,
352 },
353 storage_logs: {
354 ..._SQL_FILTER_COMMON,
355 },
356 postgrest_logs: {
357 ..._SQL_FILTER_COMMON,
358 database: (value: string) => `identifier = '${value}'`,
359 },
360 pgbouncer_logs: {
361 ..._SQL_FILTER_COMMON,
362 },
363 supavisor_logs: {
364 ..._SQL_FILTER_COMMON,
365 database: (value: string) => `m.project like '${value}%'`,
366 },
367 pg_upgrade_logs: {
368 ..._SQL_FILTER_COMMON,
369 },
370 pg_cron_logs: {
371 ..._SQL_FILTER_COMMON,
372 },
373 etl_replication_logs: {
374 ..._SQL_FILTER_COMMON,
375 pipeline_id: (value: string | number) => `pipeline_id = ${value}`,
376 },
377}
378
379export enum LogsTableName {
380 EDGE = 'edge_logs',
381 POSTGRES = 'postgres_logs',
382 FUNCTIONS = 'function_logs',
383 FN_EDGE = 'function_edge_logs',
384 AUTH = 'auth_logs',
385 AUTH_AUDIT = 'auth_audit_logs',
386 REALTIME = 'realtime_logs',
387 STORAGE = 'storage_logs',
388 POSTGREST = 'postgrest_logs',
389 SUPAVISOR = 'supavisor_logs',
390 PGBOUNCER = 'pgbouncer_logs',
391 PG_UPGRADE = 'pg_upgrade_logs',
392 PG_CRON = 'pg_cron_logs',
393 ETL = 'etl_replication_logs',
394}
395
396export const LOGS_TABLES = {
397 api: LogsTableName.EDGE,
398 database: LogsTableName.POSTGRES,
399 functions: LogsTableName.FUNCTIONS,
400 fn_edge: LogsTableName.FN_EDGE,
401 auth: LogsTableName.AUTH,
402 realtime: LogsTableName.REALTIME,
403 storage: LogsTableName.STORAGE,
404 postgrest: LogsTableName.POSTGREST,
405 supavisor: LogsTableName.SUPAVISOR,
406 pg_upgrade: LogsTableName.PG_UPGRADE,
407 pg_cron: LogsTableName.POSTGRES,
408 pgbouncer: LogsTableName.PGBOUNCER,
409 etl: LogsTableName.ETL,
410}
411
412export const LOGS_SOURCE_DESCRIPTION = {
413 [LogsTableName.EDGE]: 'Logs obtained from the network edge, containing all API requests',
414 [LogsTableName.POSTGRES]: 'Database logs obtained directly from Postgres',
415 [LogsTableName.FUNCTIONS]: 'Function logs generated from runtime execution',
416 [LogsTableName.FN_EDGE]: 'Function call logs, containing the request and response',
417 [LogsTableName.AUTH]: 'Errors, warnings, and performance details from the auth service',
418 [LogsTableName.AUTH_AUDIT]: 'Audit records of user signups, logins, and account changes',
419 [LogsTableName.REALTIME]: 'Realtime server for Postgres logical replication broadcasting',
420 [LogsTableName.STORAGE]: 'Object storage logs',
421 [LogsTableName.POSTGREST]: 'RESTful API web server logs',
422 [LogsTableName.SUPAVISOR]: 'Shared connection pooler logs for PostgreSQL',
423 [LogsTableName.PGBOUNCER]: 'Dedicated connection pooler for PostgreSQL',
424 [LogsTableName.PG_UPGRADE]: 'Logs generated by the Postgres version upgrade process',
425 [LogsTableName.PG_CRON]: 'Postgres logs from pg_cron cron jobs',
426 [LogsTableName.ETL]: 'Logs from the replication process',
427}
428
429export const FILTER_OPTIONS: FilterTableSet = {
430 // Postgres logs
431 postgres_logs: {
432 severity: {
433 label: 'Severity',
434 key: 'severity',
435 options: [
436 {
437 key: 'error',
438 label: 'Error',
439 description: 'Show all events with ERROR, PANIC, or FATAL',
440 },
441 {
442 key: 'noError',
443 label: 'No Error',
444 description: 'Show all non-error events',
445 },
446 {
447 key: 'log',
448 label: 'Log',
449 description: 'Show all events that are log severity',
450 },
451 ],
452 },
453 },
454
455 // Edge logs
456 edge_logs: {
457 status_code: {
458 label: 'Status',
459 key: 'status_code',
460 options: [
461 {
462 key: 'error',
463 label: 'Error',
464 description: '500 error codes',
465 },
466 {
467 key: 'success',
468 label: 'Success',
469 description: '200 codes',
470 },
471 {
472 key: 'warning',
473 label: 'Warning',
474 description: '400 codes',
475 },
476 ],
477 },
478 product: {
479 label: 'Product',
480 key: 'product',
481 options: [
482 {
483 key: 'database',
484 label: 'Database',
485 description: '',
486 },
487 {
488 key: 'auth',
489 label: 'Auth',
490 description: '',
491 },
492 {
493 key: 'storage',
494 label: 'Storage',
495 description: '',
496 },
497 {
498 key: 'realtime',
499 label: 'Realtime',
500 description: '',
501 },
502 ],
503 },
504 method: {
505 label: 'Method',
506 key: 'method',
507 options: [
508 {
509 key: 'get',
510 label: 'GET',
511 description: '',
512 },
513 {
514 key: 'options',
515 label: 'OPTIONS',
516 description: '',
517 },
518 {
519 key: 'put',
520 label: 'PUT',
521 description: '',
522 },
523 {
524 key: 'post',
525 label: 'POST',
526 description: '',
527 },
528 {
529 key: 'patch',
530 label: 'PATCH',
531 description: '',
532 },
533 {
534 key: 'delete',
535 label: 'DELETE',
536 description: '',
537 },
538 ],
539 },
540 },
541 // function_edge_logs
542 ...(IS_PLATFORM
543 ? {
544 function_edge_logs: {
545 status_code: {
546 label: 'Status',
547 key: 'status_code',
548 options: [
549 {
550 key: 'error',
551 label: 'Error',
552 description: '500 error codes',
553 },
554 {
555 key: 'success',
556 label: 'Success',
557 description: '200 codes',
558 },
559 {
560 key: 'warning',
561 label: 'Warning',
562 description: '400 codes',
563 },
564 ],
565 },
566 },
567 }
568 : {}),
569 // function_logs
570 function_logs: {
571 severity: {
572 label: 'Severity',
573 key: 'severity',
574 options: [
575 {
576 key: 'error',
577 label: 'Error',
578 description: 'Show all events that are "error" severity',
579 },
580 {
581 key: 'warn',
582 label: 'Warning',
583 description: 'Show all events that are "warn" severity',
584 },
585 {
586 key: 'info',
587 label: 'Info',
588 description: 'Show all events that are "info" severity',
589 },
590 {
591 key: 'debug',
592 label: 'Debug',
593 description: 'Show all events that are "debug" severity',
594 },
595 {
596 key: 'log',
597 label: 'Log',
598 description: 'Show all events that are "log" severity',
599 },
600 ],
601 },
602 },
603
604 // auth logs
605 auth_logs: {
606 severity: {
607 label: 'Severity',
608 key: 'severity',
609 options: [
610 {
611 key: 'error',
612 label: 'Error',
613 description: 'Show all events that have error or fatal severity',
614 },
615 {
616 key: 'warning',
617 label: 'Warning',
618 description: 'Show all events that have warning severity',
619 },
620 {
621 key: 'info',
622 label: 'Info',
623 description: 'Show all events that have info severity',
624 },
625 ],
626 },
627 status_code: {
628 label: 'Status Code',
629 key: 'status_code',
630 options: [
631 {
632 key: 'server_error',
633 label: 'Server Error',
634 description: 'Show all requests with 5XX status code',
635 },
636 {
637 key: 'client_error',
638 label: 'Client Error',
639 description: 'Show all requests with 4XX status code',
640 },
641 {
642 key: 'redirection',
643 label: 'Redirection',
644 description: 'Show all requests that have 3XX status code',
645 },
646 {
647 key: 'success',
648 label: 'Success',
649 description: 'Show all requests that have 2XX status code',
650 },
651 ],
652 },
653 endpoints: {
654 label: 'Endpoints',
655 key: 'endpoints',
656 options: [
657 {
658 key: 'admin',
659 label: 'Admin',
660 description: 'Show all admin requests',
661 },
662 {
663 key: 'signup',
664 label: 'Sign up',
665 description: 'Show all signup and authorization requests',
666 },
667 {
668 key: 'recover',
669 label: 'Password Recovery',
670 description: 'Show all password recovery requests',
671 },
672 {
673 key: 'authentication',
674 label: 'Authentication',
675 description: 'Show all authentication flow requests (login, otp, and Oauth2)',
676 },
677 {
678 key: 'user',
679 label: 'User',
680 description: 'Show all user data requests',
681 },
682 {
683 key: 'logout',
684 label: 'Logout',
685 description: 'Show all logout requests',
686 },
687 ],
688 },
689 },
690}
691
692export const LOGS_TAILWIND_CLASSES = {
693 log_selection_x_padding: 'px-8',
694 space_y: 'px-6',
695}
696
697export const PREVIEWER_DATEPICKER_HELPERS: DatetimeHelper[] = [
698 {
699 text: 'Last 15 minutes',
700 calcFrom: () => dayjs().subtract(15, 'minute').toISOString(),
701 calcTo: () => '',
702 },
703 {
704 text: 'Last 30 minutes',
705 calcFrom: () => dayjs().subtract(30, 'minute').toISOString(),
706 calcTo: () => '',
707 },
708 {
709 text: 'Last hour',
710 calcFrom: () => dayjs().subtract(1, 'hour').toISOString(),
711 calcTo: () => '',
712 default: true,
713 },
714 {
715 text: 'Last 3 hours',
716 calcFrom: () => dayjs().subtract(3, 'hour').toISOString(),
717 calcTo: () => '',
718 },
719 {
720 text: 'Last 24 hours',
721 calcFrom: () => dayjs().subtract(1, 'day').toISOString(),
722 calcTo: () => '',
723 },
724 {
725 text: 'Last 2 days',
726 calcFrom: () => dayjs().subtract(2, 'day').toISOString(),
727 calcTo: () => '',
728 },
729 {
730 text: 'Last 3 days',
731 calcFrom: () => dayjs().subtract(3, 'day').toISOString(),
732 calcTo: () => '',
733 },
734 {
735 text: 'Last 5 days',
736 calcFrom: () => dayjs().subtract(5, 'day').toISOString(),
737 calcTo: () => '',
738 },
739]
740export const EXPLORER_DATEPICKER_HELPERS: DatetimeHelper[] = [
741 {
742 text: 'Last hour',
743 calcFrom: () => dayjs().subtract(1, 'hour').toISOString(),
744 calcTo: () => '',
745 default: true,
746 },
747 {
748 text: 'Last 3 hours',
749 calcFrom: () => dayjs().subtract(3, 'hour').toISOString(),
750 calcTo: () => '',
751 },
752 {
753 text: 'Last 24 hours',
754 calcFrom: () => dayjs().subtract(1, 'day').toISOString(),
755 calcTo: () => '',
756 },
757 {
758 text: 'Last 3 days',
759 calcFrom: () => dayjs().subtract(3, 'day').toISOString(),
760 calcTo: () => '',
761 },
762 {
763 text: 'Last 7 days',
764 calcFrom: () => dayjs().subtract(7, 'day').toISOString(),
765 calcTo: () => '',
766 },
767]
768
769export const getDefaultHelper = (helpers: DatetimeHelper[]) =>
770 helpers.find((helper) => helper.default) || helpers[0]
771
772export const TIER_QUERY_LIMITS: {
773 [x: string]: { text: string; value: 1 | 7 | 28 | 90; unit: 'day'; promptUpgrade: boolean }
774} = {
775 FREE: { text: '1 day', value: 1, unit: 'day', promptUpgrade: true },
776 PRO: { text: '7 days', value: 7, unit: 'day', promptUpgrade: true },
777 PAYG: { text: '7 days', value: 7, unit: 'day', promptUpgrade: true },
778 TEAM: { text: '28 days', value: 28, unit: 'day', promptUpgrade: true },
779 ENTERPRISE: { text: '90 days', value: 90, unit: 'day', promptUpgrade: false },
780 PLATFORM: { text: '1 day', value: 1, unit: 'day', promptUpgrade: false },
781}
782
783export const LOG_ROUTES_WITH_REPLICA_SUPPORT = [
784 '/project/[ref]/logs/edge-logs',
785 '/project/[ref]/logs/pooler-logs',
786 '/project/[ref]/logs/postgres-logs',
787 '/project/[ref]/logs/postgrest-logs',
788]