Merge pull request #13166 from supabase/feat/local-logs

feat: initial local logflare integration
This commit is contained in:
Ziinc authored and GitHub committed 2023-03-30 12:55:09 +08:00
commit a07b2dbcd7
8 files changed
+131 -68

No files matched your search

+7
View File
@@ -78,3 +78,10 @@ SUPABASE_PUBLIC_URL=http://localhost:8000
# Enable webp support
IMGPROXY_ENABLE_WEBP_DETECTION=true
# Configure Logflare API Calls
LOGFLARE_API_KEY=api-key
LOGFLARE_URL=http://localhost:54329
# Enable logging and observability in the UI
NEXT_PUBLIC_ENABLE_LOGS=true
+3 -1
View File
@@ -9,9 +9,11 @@ SUPABASE_PUBLIC_URL=http://localhost:8000
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE
SUPABASE_SERVICE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q
SENTRY_IGNORE_API_RESOLUTION_ERROR=1
LOGFLARE_URL=http://localhost:54329
LOGFLARE_API_KEY=api-key
# TODO: evaluate these variables at runtime instead
# https://nextjs.org/docs/basic-features/environment-variables#loading-environment-variables
NEXT_PUBLIC_SITE_URL=http://localhost:3000
NEXT_PUBLIC_GOTRUE_URL=$SUPABASE_PUBLIC_URL/auth/v1
NEXT_PUBLIC_HCAPTCHA_SITE_KEY=10000000-ffff-ffff-ffff-000000000001
NEXT_PUBLIC_ENABLE_LOGS=true
@@ -1,4 +1,5 @@
import dayjs from 'dayjs'
import { IS_PLATFORM } from 'lib/constants'
import { DatetimeHelper, FilterTableSet, LogTemplate } from '.'
export const LOGS_EXPLORER_DOCS_URL =
@@ -665,3 +666,5 @@ export const TIER_QUERY_LIMITS: {
TEAM: { text: '90 days', value: 90, unit: 'day', promptUpgrade: false },
ENTERPRISE: { text: '90 days', value: 90, unit: 'day', promptUpgrade: false },
}
export const SHOW_O11Y = IS_PLATFORM ? true : process.env.NEXT_PUBLIC_ENABLE_LOGS == 'true'
@@ -14,6 +14,7 @@ import Link from 'next/link'
import React from 'react'
import { checkPermissions } from 'hooks'
import { useProfileQuery } from 'data/profile/profile-query'
import { IS_PLATFORM } from 'lib/constants'
export interface LogsQueryPanelProps {
templates?: LogTemplate[]
@@ -130,7 +131,7 @@ const LogsQueryPanel = ({
<Button type="default" onClick={onClear}>
Clear query
</Button>
{onSave && (
{IS_PLATFORM && onSave && (
<Tooltip.Root delayDuration={0}>
<Tooltip.Trigger>
<Button
@@ -1,75 +1,75 @@
import { Project } from 'types'
import { ProductMenuGroup } from 'components/ui/ProductMenu/ProductMenu.types'
import { ProductMenuGroup, ProductMenuGroupItem } from 'components/ui/ProductMenu/ProductMenu.types'
import { IS_PLATFORM } from 'lib/constants'
export const generateLogsMenu = (project?: Project): ProductMenuGroup[] => {
const ref = project?.ref ?? 'default'
return [
...(IS_PLATFORM
? [
{
title: 'Logs Explorer',
items: [
{ key: 'explorer', name: 'Query', root: true },
{ key: 'saved', name: 'Saved Queries' },
{ key: 'recent', name: 'Recent Queries' },
{ key: 'templates', name: 'Templates' },
].map(({ key, name, root }) => ({
name,
key,
url: `/project/${ref}/logs/explorer${root ? '' : '/' + key}`,
{
title: 'Logs Explorer',
items: (
[
{ key: 'explorer', name: 'Query', root: true },
IS_PLATFORM ? { key: 'saved', name: 'Saved Queries' } : null,
{ key: 'recent', name: 'Recent Queries' },
{ key: 'templates', name: 'Templates' },
].filter((item) => item) as { name: string; key: string; root: boolean }[]
).map(({ key, name, root }) => ({
name,
key,
url: `/project/${ref}/logs/explorer${root ? '' : '/' + key}`,
items: [],
})),
},
{
items: [
{
name: IS_PLATFORM ? 'API Edge Network' : 'Kong API',
key: 'edge-logs',
url: `/project/${ref}/logs/edge-logs`,
items: [],
},
{
name: 'Postgres',
key: 'postgres-logs',
url: `/project/${ref}/logs/postgres-logs`,
items: [],
},
{
name: 'PostgREST',
key: 'postgrest-logs',
url: `/project/${ref}/logs/postgrest-logs`,
items: [],
},
IS_PLATFORM
? {
name: 'PgBouncer',
key: 'pgbouncer-logs',
url: `/project/${ref}/logs/pgbouncer-logs`,
items: [],
})),
},
{
items: [
{
name: 'API Edge Network',
key: 'edge-logs',
url: `/project/${ref}/logs/edge-logs`,
items: [],
},
{
name: 'Postgres',
key: 'postgres-logs',
url: `/project/${ref}/logs/postgres-logs`,
items: [],
},
{
name: 'PostgREST',
key: 'postgrest-logs',
url: `/project/${ref}/logs/postgrest-logs`,
items: [],
},
{
name: 'PgBouncer',
key: 'pgbouncer-logs',
url: `/project/${ref}/logs/pgbouncer-logs`,
items: [],
},
{
name: 'Auth',
key: 'auth-logs',
url: `/project/${ref}/logs/auth-logs`,
items: [],
},
{
name: 'Storage',
key: 'storage-logs',
url: `/project/${ref}/logs/storage-logs`,
items: [],
},
{
name: 'Realtime',
key: 'realtime-logs',
url: `/project/${ref}/logs/realtime-logs`,
items: [],
},
],
},
]
: []),
}
: null,
,
{
name: 'Auth',
key: 'auth-logs',
url: `/project/${ref}/logs/auth-logs`,
items: [],
},
{
name: 'Storage',
key: 'storage-logs',
url: `/project/${ref}/logs/storage-logs`,
items: [],
},
{
name: 'Realtime',
key: 'realtime-logs',
url: `/project/${ref}/logs/realtime-logs`,
items: [],
},
].filter((item) => item) as ProductMenuGroupItem[],
},
]
}
@@ -13,6 +13,7 @@ import SVG from 'react-inlinesvg'
import { ProjectBase } from 'types'
import { Route } from 'components/ui/ui.types'
import { BASE_PATH, IS_PLATFORM, PROJECT_STATUS } from 'lib/constants'
import { SHOW_O11Y } from 'components/interfaces/Settings/Logs'
export const generateToolRoutes = (ref?: string, project?: ProjectBase): Route[] => {
const isProjectBuilding = project?.status === PROJECT_STATUS.COMING_UP
@@ -139,7 +140,7 @@ export const generateOtherRoutes = (ref?: string, project?: ProjectBase): Route[
},
]
: []),
...(IS_PLATFORM
...(SHOW_O11Y
? [
{
key: 'logs',
+3
View File
@@ -1,5 +1,8 @@
const PUBLIC_URL = new URL(process.env.SUPABASE_PUBLIC_URL || 'http://localhost:8000')
// Use LOGFLARE_URL until analytics/v1/ routing is supported
export const PROJECT_ANALYTICS_URL = `${process.env.LOGFLARE_URL}/api/`
export const PROJECT_REST_URL = `${PUBLIC_URL.origin}/rest/v1/`
export const PROJECT_ENDPOINT = PUBLIC_URL.host
export const PROJECT_ENDPOINT_PROTOCOL = PUBLIC_URL.protocol.replace(':', '')
@@ -0,0 +1,46 @@
import { NextApiRequest, NextApiResponse } from 'next'
import apiWrapper from 'lib/api/apiWrapper'
import { PROJECT_ANALYTICS_URL } from 'pages/api/constants'
import { get } from 'lib/common/fetch'
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
async function handler(req: NextApiRequest, res: NextApiResponse) {
const { method } = req
switch (method) {
case 'GET':
const missingEnvVars = [
process.env.LOGFLARE_API_KEY ? null : 'LOGFLARE_API_KEY',
process.env.LOGFLARE_URL ? null : 'LOGFLARE_URL',
].filter((v) => v)
if (missingEnvVars.length == 0) {
const result = await proxyRequest(req)
return res.status(200).json(result)
} else {
return res
.status(500)
.json({ error: { message: `${missingEnvVars.join(', ')} env variables are not set` } })
}
default:
res.setHeader('Allow', ['GET'])
res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
}
}
const proxyRequest = async (req: NextApiRequest) => {
const { name, ...toForward } = req.query
const payload = { ...toForward, project_tier: 'ENTERPRISE' }
const search = '?' + new URLSearchParams(payload as any).toString()
const apiKey = process.env.LOGFLARE_API_KEY
const url = `${PROJECT_ANALYTICS_URL}endpoints/query/name/${name}${search}`
const result = await get(url, {
headers: {
'x-api-key': apiKey,
'Content-Type': 'application/json',
Accept: 'application/json',
},
})
return result
}