Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: service logs in dashboard #844

Merged
merged 32 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a68ab4f
feat: service logs
davemooreuws Jan 9, 2025
0c432ff
add purge logs
davemooreuws Jan 9, 2025
130af7f
fix some purge issues
davemooreuws Jan 9, 2025
bdb68f1
wip frontend
davemooreuws Jan 9, 2025
f8df5cf
fix issue with multiple consumers of updates channel
davemooreuws Jan 9, 2025
ee8e0ad
fix styling
davemooreuws Jan 9, 2025
4704466
format common ansi foreground colors
davemooreuws Jan 10, 2025
ebdf85e
fix lint error
davemooreuws Jan 10, 2025
98488f2
format
davemooreuws Jan 10, 2025
2e381d9
tests for logs
davemooreuws Jan 10, 2025
6beae13
change logs icon
davemooreuws Jan 10, 2025
7563db4
change service to origin for logs to cater for system messages
davemooreuws Jan 10, 2025
4a2474f
add more information to logs time tooltip
davemooreuws Jan 10, 2025
baf7823
remove test that aren't on nitric run
davemooreuws Jan 10, 2025
8a0c816
rename column header
davemooreuws Jan 10, 2025
ada1286
make run and start logs consistent
davemooreuws Jan 10, 2025
84dbc94
prevent empty message logs
davemooreuws Jan 10, 2025
bd7e07e
make run and start logs consistent 2
davemooreuws Jan 10, 2025
e3af4ff
add error log levels
davemooreuws Jan 13, 2025
d45d9a9
backend filters and sorting
davemooreuws Jan 14, 2025
6adeb98
wip frontend
davemooreuws Jan 14, 2025
081c67a
format
davemooreuws Jan 14, 2025
2f8b3ef
fix existing tests
davemooreuws Jan 14, 2025
8689071
implement filters via url params
davemooreuws Jan 15, 2025
3761235
handle warnings
davemooreuws Jan 15, 2025
d155e9b
fix lint
davemooreuws Jan 15, 2025
414ca56
add more tests for log filters
davemooreuws Jan 15, 2025
b93a438
ensure invalid url params are ignored
davemooreuws Jan 15, 2025
a98d643
Merge pull request #1 from davemooreuws/feat/logs-filter
davemooreuws Jan 15, 2025
80eee7b
convert all ansi to css
davemooreuws Jan 16, 2025
6f3316f
virtualize the logs list
davemooreuws Jan 16, 2025
cc9b7b5
fix tests
davemooreuws Jan 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

tea "github.com/charmbracelet/bubbletea"
"github.com/samber/lo"
"github.com/sirupsen/logrus"
"github.com/spf13/afero"
"github.com/spf13/cobra"

Expand Down Expand Up @@ -87,6 +88,9 @@ var runCmd = &cobra.Command{
tui.CheckErr(err)
defer logWriter.Close()

// Initialize the system service log logger
system.InitializeServiceLogger(proj.Directory)

teaOptions := []tea.ProgramOption{}
if isNonInteractive() {
teaOptions = append(teaOptions, tea.WithoutRenderer(), tea.WithInput(nil))
Expand Down Expand Up @@ -207,10 +211,20 @@ var runCmd = &cobra.Command{
close(stopChan)
}()

logger := system.GetServiceLogger()

for {
select {
case update := <-allUpdates:
fmt.Printf("%s [%s]: %s", update.ServiceName, update.Status, update.Message)
// Write log to file
level := logrus.InfoLevel

if update.Status == project.ServiceRunStatus_Error {
level = logrus.ErrorLevel
}

logger.WriteLog(level, update.Message, update.Label)
case <-stopChan:
fmt.Println("Shutting down services - exiting")
return nil
Expand Down
14 changes: 14 additions & 0 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/samber/lo"
"github.com/sirupsen/logrus"
"github.com/spf13/afero"
"github.com/spf13/cobra"

Expand Down Expand Up @@ -167,6 +168,9 @@ var startCmd = &cobra.Command{
tui.CheckErr(err)
defer logWriter.Close()

// Initialize the system service log logger
system.InitializeServiceLogger(proj.Directory)

teaOptions := []tea.ProgramOption{}
if isNonInteractive() {
teaOptions = append(teaOptions, tea.WithoutRenderer(), tea.WithInput(nil))
Expand Down Expand Up @@ -265,10 +269,20 @@ var startCmd = &cobra.Command{
close(stopChan)
}()

logger := system.GetServiceLogger()

for {
select {
case update := <-allUpdates:
fmt.Printf("%s [%s]: %s", update.ServiceName, update.Status, update.Message)
// Write log to file
level := logrus.InfoLevel

if update.Status == project.ServiceRunStatus_Error {
level = logrus.ErrorLevel
}

logger.WriteLog(level, update.Message, update.Label)
case <-stopChan:
fmt.Println("Shutting down services - exiting")
return nil
Expand Down
2 changes: 2 additions & 0 deletions pkg/dashboard/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,8 @@ func (d *Dashboard) Start() error {

http.HandleFunc("/api/ws-clear-messages", d.handleWebsocketMessagesClear())

http.HandleFunc("/api/logs", d.createServiceLogsHandler(d.project))

d.wsWebSocket.HandleConnect(func(s *melody.Session) {
// Send a welcome message to the client
err := d.sendWebsocketsUpdate()
Expand Down
2 changes: 2 additions & 0 deletions pkg/dashboard/frontend/cypress/e2e/a11y.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ describe('a11y test suite', () => {
'/storage',
'/secrets',
'/topics',
'/jobs',
'/websockets',
'/logs',
'/not-found',
]

Expand Down
91 changes: 91 additions & 0 deletions pkg/dashboard/frontend/cypress/e2e/logs.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
describe('logs test suite', () => {
beforeEach(() => {
cy.viewport('macbook-16')

cy.visit('/logs')

cy.wait(500)
})

it(`Should create logs`, () => {
cy.getTestEl('logs').children().should('have.length.above', 2)

const expectedMessages = [
'started service services/my-test-secret.ts',
'started service services/my-test-service.ts',
'started service services/my-test-db.ts',
]

expectedMessages.forEach((message) => {
cy.getTestEl('logs').should('contain.text', message)
})
})

it(`Should search with correct results`, () => {
cy.getTestEl('logs').children().should('have.length.above', 2)

cy.getTestEl('log-search').type(
'started service services/my-test-secret.ts',
)

cy.getTestEl('logs').children().should('have.length', 1)

cy.getTestEl('log-search').clear()

cy.getTestEl('logs').children().should('have.length.above', 2)
})

it(`Should filter origin with correct results`, () => {
cy.getTestEl('logs').children().should('have.length.above', 2)

cy.getTestEl('filter-logs-btn').click()

cy.getTestEl('filter-origin-collapsible').click()

cy.getTestEl('origin-select').click()

cy.get('div[data-value="nitric"]').click()

cy.getTestEl('logs').children().should('have.length', 3)

cy.getTestEl('filter-logs-reset-btn').click()

cy.getTestEl('logs').children().should('have.length.above', 2)
})

it(`Should pre-populate filters via url param`, () => {
cy.visit(
'/logs?origin=nitric%2Cservices/my-test-db.ts&level=info&timeline=pastHour',
)

cy.getTestEl('filter-logs-btn').click()

cy.getTestEl('filter-timeline-collapsible').click()
cy.getTestEl('filter-contains-level-collapsible').click()
cy.getTestEl('filter-origin-collapsible').click()

cy.getTestEl('timeline-select-trigger').should('contain.text', 'Past Hour')
cy.getTestEl('level-select').should('contain.text', 'Info')
cy.getTestEl('origin-select').should(
'contain.text',
'nitricservices/my-test-db.ts',
)
})

it(`Should purge logs`, () => {
cy.getTestEl('logs').children().should('have.length.above', 2)

cy.intercept('DELETE', '/api/logs').as('purge')

cy.getTestEl('log-options-btn').click()

cy.getTestEl('purge-logs-btn').click()

cy.wait('@purge')

cy.getTestEl('logs')
.children()
.first()
.should('have.text', 'No logs available')
})
})
17 changes: 11 additions & 6 deletions pkg/dashboard/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,28 @@
"@codemirror/lint": "^6.8.1",
"@dagrejs/dagre": "^1.0.4",
"@fontsource/archivo": "^4.5.11",
"@fontsource/jetbrains-mono": "^5.1.2",
"@fontsource/sora": "^4.5.12",
"@heroicons/react": "^2.1.1",
"@prantlf/jsonlint": "^14.0.3",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-checkbox": "^1.1.1",
"@radix-ui/react-collapsible": "^1.1.2",
"@radix-ui/react-context-menu": "^2.2.1",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.4",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-scroll-area": "^1.0.5",
"@radix-ui/react-select": "^1.2.2",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-separator": "^1.1.1",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-tooltip": "^1.0.7",
"@radix-ui/react-tooltip": "^1.1.6",
"@tailwindcss/forms": "^0.5.7",
"@tanstack/react-table": "^8.20.1",
"@tanstack/react-virtual": "^3.11.2",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@uiw/codemirror-extensions-langs": "^4.23.0",
Expand All @@ -50,13 +53,15 @@
"astro-fathom": "^2.0.0",
"chonky": "^2.3.2",
"chonky-icon-fontawesome": "^2.3.2",
"class-variance-authority": "^0.7.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "1.0.0",
"cronstrue": "^2.50.0",
"date-fns": "^3.6.0",
"fancy-ansi": "^0.1.3",
"html-to-image": "^1.11.11",
"lottie-react": "^2.4.0",
"lucide-react": "^0.261.0",
"lucide-react": "^0.471.1",
"radash": "^12.1.0",
"react": "^18.3.1",
"react-complex-tree": "^2.4.4",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { useState } from 'react'
import type { NavigationItemProps } from './NavigationItem'
import NavigationItem from './NavigationItem'
import { Separator } from '@/components/ui/separator'
import { HeartIcon, MapIcon } from '@heroicons/react/24/outline'
import { ListBulletIcon, HeartIcon, MapIcon } from '@heroicons/react/24/outline'

interface NavigationBarProps {
navigation: Omit<NavigationItemProps, 'routePath'>[]
Expand Down Expand Up @@ -46,6 +46,13 @@ const NavigationBar: React.FC<NavigationBarProps> = ({
href="/architecture"
onClick={closeNavigation}
/>
<NavigationItem
routePath={routePath}
icon={ListBulletIcon}
name="Logs"
href="/logs"
onClick={closeNavigation}
/>
</ul>
<Separator />
<ul className="flex flex-col justify-start gap-y-1 px-4">
Expand Down
Loading
Loading