Skip to content

Conversation

@benjaminch
Copy link
Collaborator

Summary

Issue: Ticket: QOV-1430

This PR allows users to retrieve Envoy logs for their services as they can already do for Nginx.

Screenshots / Recordings

image image

Testing

  • Changes tested locally in the relevant Console's pages and Storybooks
  • yarn test or yarn test -u (if you need to regenerate snapshots)
  • yarn format
  • yarn lint

PR Checklist

  • I followed naming, styling, and TypeScript rules (see .cursor/rules)
  • I performed a self-review (diff inspected, dead code removed)
  • I titled the PR using Conventional Commits with a scope when possible (e.g. feat(service): add new Terraform service) - required for semantic-release
  • I only kept necessary comments, written in English (watch for useless AI comments)
  • I involved a designer to validate UI changes if I am not a designer
  • I covered new business logic with tests (unit)
  • I confirmed CI is green (Codecov red can be accepted)
  • I reviewed and executed locally any AI-assisted code

@benjaminch benjaminch added the enhancement New feature or request label Jan 23, 2026
@RemiBonnet
Copy link
Member

Qovery Preview

Qovery can create a Preview Environment for this PR.
To trigger its creation, please post a comment with one of the following command.

Command Blueprint environment
/qovery preview cc1de7de-94e5-40f8-8e45-bc8986d9dfec storybook
/qovery preview 28c47145-c8e7-4b9d-8d9e-c65c95b48425 staging
/qovery preview {all|UUID1,UUID2,...} To preview multiple environments

This comment has been generated from Qovery AI 🤖.
Below, a word from its wisdom :

Truth can only be found in one place: the code

@benjaminch
Copy link
Collaborator Author

/qovery preview 28c47145-c8e7-4b9d-8d9e-c65c95b48425

@RemiBonnet
Copy link
Member

Qovery Preview

A preview environment was automatically created via Qovery.
Click on the link below to follow its deployment and use it.
👉 [PR] staging - chore(service): allow to retrieve envoy gateway logs - 2026-01-23T16:12:30Z

Another comment will be posted when deployments are finished

@codecov
Copy link

codecov bot commented Jan 23, 2026

Codecov Report

❌ Patch coverage is 51.11111% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 49.09%. Comparing base (6358421) to head (ab4a64d).

Files with missing lines Patch % Lines
...e-service-history-logs/use-service-history-logs.ts 31.25% 8 Missing and 3 partials ⚠️
...oks/use-service-live-logs/use-service-live-logs.ts 64.70% 4 Missing and 2 partials ⚠️
...rc/lib/search-service-logs/search-service-logs.tsx 16.66% 4 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           staging    #2326      +/-   ##
===========================================
+ Coverage    47.73%   49.09%   +1.35%     
===========================================
  Files         1254      922     -332     
  Lines        22717    18523    -4194     
  Branches      6635     5466    -1169     
===========================================
- Hits         10844     9093    -1751     
+ Misses        9808     7709    -2099     
+ Partials      2065     1721     -344     
Flag Coverage Δ
unittests 49.09% <51.11%> (+1.35%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@RemiBonnet
Copy link
Member

Qovery Preview

Your preview environment has been successfully deployed !
Click on the link below to open your service:
👉 console

@benjaminch benjaminch force-pushed the chore/QOV-1430/allow-to-retrieve-envoy-gateway-logs branch from dcafb9a to ab4a64d Compare January 23, 2026 17:49
@benjaminch benjaminch requested a review from Copilot January 23, 2026 17:49
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the service logs functionality to support retrieving Envoy Gateway logs, mirroring the existing capability for Nginx logs. Users can now filter and view logs from both Nginx and Envoy ingress controllers alongside their service logs.

Changes:

  • Added 'envoy' filter support throughout the logs architecture (search, context, data access)
  • Implemented Envoy-specific WebSocket subscriptions and query handling for both live and historical logs
  • Added UI elements (ENVOY badge) to distinguish Envoy logs from regular service and Nginx logs

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
search-service-logs.tsx Added 'envoy' to valid filter keys, buildValueOptions, buildQueryParams, and filter options
search-service-logs.spec.tsx Updated test to use refetchHistoryLogs parameter instead of isLoading
search-service-logs-helpers.spec.ts New test file for filter parsing logic (tests inline logic, not implementation)
service-logs-context.tsx Added envoy BooleanParam to query parameters
row-service-logs.tsx Added isEnvoy detection, ENVOY badge, and conditional rendering logic matching nginx
row-service-logs.spec.tsx Added comprehensive test coverage for envoy logs behavior
use-service-live-logs.ts Added envoy WebSocket subscription with separate query and log handler
use-service-history-logs.ts Added envoy query support, log accumulation, and refetch logic
domains-service-logs-data-access.ts Added LogType type ('service' | 'nginx' | 'envoy'), updated buildLokiQuery to support envoy
domains-service-logs-data-access.spec.ts New comprehensive test file for buildLokiQuery with all log types

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1 to +149
describe('SearchServiceLogs helpers', () => {
describe('filter parsing', () => {
it('should parse nginx filter', () => {
const input = 'nginx:true'
const filterRegex = /(\w+)[:]([^\s]*)/g
const matches = input.match(filterRegex)

expect(matches).toEqual(['nginx:true'])

const parts = matches?.[0].split(/[:]/)
expect(parts?.[0]).toBe('nginx')
expect(parts?.[1]).toBe('true')
})

it('should parse envoy filter', () => {
const input = 'envoy:true'
const filterRegex = /(\w+)[:]([^\s]*)/g
const matches = input.match(filterRegex)

expect(matches).toEqual(['envoy:true'])

const parts = matches?.[0].split(/[:]/)
expect(parts?.[0]).toBe('envoy')
expect(parts?.[1]).toBe('true')
})

it('should parse multiple filters including nginx', () => {
const input = 'level:error nginx:true'
const filterRegex = /(\w+)[:]([^\s]*)/g
const matches = input.match(filterRegex)

expect(matches).toEqual(['level:error', 'nginx:true'])
})

it('should parse multiple filters including envoy', () => {
const input = 'level:error envoy:true'
const filterRegex = /(\w+)[:]([^\s]*)/g
const matches = input.match(filterRegex)

expect(matches).toEqual(['level:error', 'envoy:true'])
})

it('should parse instance filter with envoy', () => {
const input = 'instance:envoy-pod-123 envoy:true'
const filterRegex = /(\w+)[:]([^\s]*)/g
const matches = input.match(filterRegex)

expect(matches).toEqual(['instance:envoy-pod-123', 'envoy:true'])
})

it('should parse search text with nginx filter', () => {
const input = 'nginx:true error occurred'
const filterRegex = /(\w+)[:]([^\s]*)/g
const textWithoutFilters = input.replace(filterRegex, '').trim()

expect(textWithoutFilters).toBe('error occurred')
})

it('should parse search text with envoy filter', () => {
const input = 'envoy:true upstream timeout'
const filterRegex = /(\w+)[:]([^\s]*)/g
const textWithoutFilters = input.replace(filterRegex, '').trim()

expect(textWithoutFilters).toBe('upstream timeout')
})

it('should handle both nginx and envoy filters together', () => {
const input = 'nginx:true envoy:true level:error'
const filterRegex = /(\w+)[:]([^\s]*)/g
const matches = input.match(filterRegex)

expect(matches).toEqual(['nginx:true', 'envoy:true', 'level:error'])
})
})

describe('valid filter keys', () => {
const VALID_FILTER_KEYS = ['level', 'instance', 'message', 'nginx', 'envoy', 'search', 'deploymentId']

it('should include nginx in valid filter keys', () => {
expect(VALID_FILTER_KEYS).toContain('nginx')
})

it('should include envoy in valid filter keys', () => {
expect(VALID_FILTER_KEYS).toContain('envoy')
})

it('should validate nginx filter key', () => {
expect(VALID_FILTER_KEYS.includes('nginx')).toBe(true)
})

it('should validate envoy filter key', () => {
expect(VALID_FILTER_KEYS.includes('envoy')).toBe(true)
})

it('should include all expected filter keys', () => {
expect(VALID_FILTER_KEYS).toEqual(
expect.arrayContaining(['level', 'instance', 'message', 'nginx', 'envoy', 'search', 'deploymentId'])
)
})
})

describe('buildValueOptions logic', () => {
it('should create nginx filter option', () => {
const queryParams = { nginx: true }
const value = 'nginx:true'

expect(value).toBe('nginx:true')
})

it('should create envoy filter option', () => {
const queryParams = { envoy: true }
const value = 'envoy:true'

expect(value).toBe('envoy:true')
})

it('should handle both nginx and envoy params', () => {
const nginxValue = 'nginx:true'
const envoyValue = 'envoy:true'

expect(nginxValue).not.toBe(envoyValue)
expect(nginxValue).toContain('nginx')
expect(envoyValue).toContain('envoy')
})
})

describe('buildQueryParams logic', () => {
it('should set nginx param to true when nginx filter is present', () => {
const filterKey = 'nginx'
const shouldBeTrue = filterKey === 'nginx'

expect(shouldBeTrue).toBe(true)
})

it('should set envoy param to true when envoy filter is present', () => {
const filterKey = 'envoy'
const shouldBeTrue = filterKey === 'envoy'

expect(shouldBeTrue).toBe(true)
})

it('should handle both nginx and envoy filters independently', () => {
const filters = ['nginx', 'envoy']

expect(filters.includes('nginx')).toBe(true)
expect(filters.includes('envoy')).toBe(true)
})
})
})
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test file 'search-service-logs-helpers.spec.ts' contains tests that don't actually test the implementation in 'search-service-logs.tsx'. The tests redefine logic inline (like VALID_FILTER_KEYS at line 77, filterRegex patterns, etc.) rather than importing and testing the actual buildValueOptions and buildQueryParams functions from the implementation. This means the tests will always pass regardless of whether the actual implementation is correct. Consider either:

  1. Exporting the helper functions (buildValueOptions, buildQueryParams, VALID_FILTER_KEYS) from search-service-logs.tsx so they can be imported and tested, or
  2. Removing this test file and adding more comprehensive tests to search-service-logs.spec.tsx that test the component behavior with envoy filters.

Copilot uses AI. Check for mistakes.
Comment on lines 35 to +36
const [enabledNginx, setEnabledNginx] = useState(false)
const [enabledEnvoy, setEnabledEnvoy] = useState(false)
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The state variables 'enabledNginx' (line 35) and 'enabledEnvoy' (line 36) along with their setters are defined and exported (lines 280-283) but are never actually used anywhere in the codebase. The WebSocket subscriptions are already properly controlled by the 'enabled' prop condition based on queryParams.nginx and queryParams.envoy. Consider removing these unused state variables unless they are intended for future use.

Suggested change
const [enabledNginx, setEnabledNginx] = useState(false)
const [enabledEnvoy, setEnabledEnvoy] = useState(false)

Copilot uses AI. Check for mistakes.
queryParams.message,
queryParams.search,
queryParams.version,
queryParams.nginx,
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dependency array includes 'queryParams.nginx' but this value is not used in the useMemo computation. The nginx parameter only affects the 'enabled' condition of the WebSocket subscription, not the query string itself. Including it here causes unnecessary recalculations of the memoized query. Consider removing 'queryParams.nginx' from this dependency array.

Suggested change
queryParams.nginx,

Copilot uses AI. Check for mistakes.
queryParams.message,
queryParams.search,
queryParams.version,
queryParams.envoy,
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dependency array includes 'queryParams.envoy' but this value is not used in the useMemo computation. The envoy parameter only affects the 'enabled' condition of the WebSocket subscription, not the query string itself. Including it here causes unnecessary recalculations of the memoized query. Consider removing 'queryParams.envoy' from this dependency array.

Suggested change
queryParams.envoy,

Copilot uses AI. Check for mistakes.
Comment on lines +104 to +111
const queryParams = { nginx: true }
const value = 'nginx:true'

expect(value).toBe('nginx:true')
})

it('should create envoy filter option', () => {
const queryParams = { envoy: true }
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable queryParams.

Suggested change
const queryParams = { nginx: true }
const value = 'nginx:true'
expect(value).toBe('nginx:true')
})
it('should create envoy filter option', () => {
const queryParams = { envoy: true }
const value = 'nginx:true'
expect(value).toBe('nginx:true')
})
it('should create envoy filter option', () => {

Copilot uses AI. Check for mistakes.
Comment on lines +104 to +111
const queryParams = { nginx: true }
const value = 'nginx:true'

expect(value).toBe('nginx:true')
})

it('should create envoy filter option', () => {
const queryParams = { envoy: true }
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable queryParams.

Suggested change
const queryParams = { nginx: true }
const value = 'nginx:true'
expect(value).toBe('nginx:true')
})
it('should create envoy filter option', () => {
const queryParams = { envoy: true }
const value = 'nginx:true'
expect(value).toBe('nginx:true')
})
it('should create envoy filter option', () => {

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants