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: logs page v2 #2701

Merged
merged 52 commits into from
Dec 5, 2024
Merged

feat: logs page v2 #2701

merged 52 commits into from
Dec 5, 2024

Conversation

ogzhanolguncu
Copy link
Contributor

@ogzhanolguncu ogzhanolguncu commented Dec 4, 2024

What does this PR do?

Fixes # (issue)

If there is not an issue for this, please create one first. This is used to tracking purposes and also helps use understand why this PR exists

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

  • Test A
  • Test B

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a LogsChart component for visualizing log data with a bar chart.
    • Added a DatePickerWithRange component for selecting custom date ranges.
    • Implemented a ResponseStatus filter component for managing HTTP response status filters.
    • Launched a SearchCombobox for enhanced log search capabilities.
    • Added a Timeline component for selecting time ranges for log filtering.
    • Introduced a LogsFilters component to streamline log entry filtering.
    • New LogFooter, LogHeader, LogMetaSection, and LogSection components for detailed log entry views.
    • Added a LoadingRow component to display loading states in the logs table.
    • Introduced a ResizablePanel for improved log detail views.
    • Added a TimestampInfo component for enhanced timestamp displays.
    • New Calendar component for date selection integrated with the UI.
    • Added a ButtonGroup component for grouping buttons in the UI.
  • Improvements

    • Enhanced log querying capabilities with new search parameters and error handling.
    • Updated the LogsTable for better performance with large datasets.
  • Bug Fixes

    • Fixed issues related to log fetching and display, ensuring accurate representation of log data.
  • Documentation

    • Updated documentation to reflect new components and features introduced in this release.

ogzhanolguncu and others added 30 commits September 26, 2024 15:57
Add virtual list to table to prevent rendering lots of items at once.

Since we store items in the memory as we fetch more item, list might get
big enough to slow the page down, thats why we have to partially show
them.
Add debounce to combobox filters and add shortcut del/backspace to
remove combobox filters when focused
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (11)
apps/dashboard/styles/tailwind/base.css (2)

14-19: LGTM! Consider adding color documentation.

The chart color variables are well-structured using HSL format. The semantic naming and color choices provide good visual distinction for data visualization.

Consider adding a comment block above these variables documenting their intended usage in charts (e.g., which chart types or data series should use which colors).


136-150: Consider using CSS variables for color values.

The code block styling implementation is clean, but the line number color is hardcoded using rgba. Consider using a CSS variable for better theme consistency.

 code .line::before {
   content: counter(step);
   counter-increment: step;
   width: 1rem;
   margin-right: 1.5rem;
   display: inline-block;
   text-align: right;
-  color: rgba(115, 138, 148, 0.4);
+  color: var(--content-subtle);
 }
apps/dashboard/app/(app)/logs/query-state.ts (3)

13-13: Consider reordering status codes logically.

The status codes array mixes success (200) and error codes (400, 500). Consider reordering them for better readability:

-export const STATUSES = [400, 500, 200] as const;
+export const STATUSES = [200, 400, 500] as const;

12-12: Consider exporting TIMELINE_OPTIONS constant.

Since TIMELINE_OPTIONS is used in the Timeline type definition, it should be exported for consistency and potential reuse.

-const TIMELINE_OPTIONS = ["1h", "3h", "6h", "12h", "24h"] as const;
+export const TIMELINE_OPTIONS = ["1h", "3h", "6h", "12h", "24h"] as const;

17-25: Add JSDoc comments for better type documentation.

Consider adding JSDoc comments to document the purpose and constraints of each field in the QuerySearchParams type.

+/**
+ * Search parameters for filtering logs
+ */
 export type QuerySearchParams = {
+  /** Host to filter logs by */
   host: string;
+  /** Specific request ID to search for */
   requestId: string;
+  /** HTTP method to filter by */
   method: string;
+  /** URL path pattern to match */
   path: string;
+  /** Array of HTTP status codes to filter by */
   responseStatuses: ResponseStatus[];
+  /** Start timestamp in milliseconds */
   startTime: number;
+  /** Optional end timestamp in milliseconds */
   endTime?: number;
 };
apps/dashboard/app/(app)/logs/components/filters/components/response-status.tsx (1)

14-18: Consider expanding HTTP status code coverage

The current implementation has several limitations:

  1. Missing 3XX (Redirection) status codes
  2. Using string IDs that are later converted to numbers
  3. Oversimplified grouping of status codes

Consider this improved implementation:

 const checkboxItems = [
-  { id: "500", label: "Error", description: "5XX error codes" },
-  { id: "200", label: "Success", description: "2XX success codes" },
-  { id: "400", label: "Warning", description: "4XX warning codes" },
+  { id: 500, label: "Server Error", description: "5XX server error codes" },
+  { id: 200, label: "Success", description: "2XX success codes" },
+  { id: 400, label: "Client Error", description: "4XX client error codes" },
+  { id: 300, label: "Redirection", description: "3XX redirection codes" },
 ];
apps/dashboard/app/(app)/logs/page.tsx (1)

28-30: Enhance the error message for better user experience

Consider providing a more user-friendly error message when workspace is not found.

-    return <div>Workspace with tenantId: {tenantId} not found</div>;
+    return <div className="text-center p-4">
+      <h2 className="text-lg font-semibold">Workspace Not Found</h2>
+      <p className="text-gray-600">The requested workspace could not be found. Please check your access permissions.</p>
+    </div>;
apps/dashboard/app/(app)/logs/components/logs-table-loading-row.tsx (1)

1-16: Consider extracting grid layout configuration

The grid column sizes are currently hardcoded and duplicated from the parent table. Consider extracting these values into shared constants to maintain consistency and prevent synchronization issues.

+const GRID_LAYOUT = "grid-cols-[166px_72px_20%_1fr]";
+
 export const LoadingRow = () => (
-  <div className="absolute w-full font-mono grid grid-cols-[166px_72px_20%_1fr] text-[13px] leading-[14px] mb-[1px] h-[26px]">
+  <div className={`absolute w-full font-mono grid ${GRID_LAYOUT} text-[13px] leading-[14px] mb-[1px] h-[26px]`}>
apps/dashboard/app/(app)/logs/components/logs-table.tsx (3)

12-14: Consider moving constants to a shared configuration file

These table-related constants might be needed by other components (like LoadingRow). Consider moving them to a shared configuration file to maintain consistency across components.


27-32: Consider optimizing virtualizer configuration

The current configuration works but could be enhanced:

  1. Consider adjusting overscan based on viewport size for better performance
  2. Memoize the estimateSize callback to prevent unnecessary recalculations
+const estimateSize = () => ROW_HEIGHT;
+
 const virtualizer = useVirtualizer({
   count: isLoading ? SKELETON_ROWS : logs?.length ?? 0,
   getScrollElement: () => parentRef.current,
-  estimateSize: () => ROW_HEIGHT,
+  estimateSize,
-  overscan: 5,
+  overscan: Math.ceil(window.innerHeight / ROW_HEIGHT),
 });

66-182: Optimize rendering performance

Consider the following performance optimizations:

  1. Memoize complex className calculations
  2. Extract inline styles to constants
  3. Use CSS Grid template areas for layout
+const VIRTUAL_ITEM_STYLE = (start: number) => ({
+  position: 'absolute',
+  top: `${start}px`,
+  width: '100%',
+});
+
+const useLogStyles = (log: Log, selectedLog: Log | null) => {
+  return useMemo(() => ({
+    className: cn(
+      "font-mono grid grid-cols-[166px_72px_20%_1fr]",
+      "text-[13px] leading-[14px] mb-[1px] rounded-[5px]",
+      "h-[26px] cursor-pointer absolute top-0 left-0 w-full",
+      "hover:bg-background-subtle/90 pl-1",
+      STATUS_STYLES[getStatusClass(log.response_status)],
+      selectedLog && {
+        "opacity-50": selectedLog.request_id !== log.request_id,
+        "opacity-100": selectedLog.request_id === log.request_id,
+      }
+    ),
+    style: VIRTUAL_ITEM_STYLE(virtualRow.start),
+  }), [log, selectedLog]);
+};
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c7f6e62 and 92ed909.

📒 Files selected for processing (11)
  • apps/dashboard/app/(app)/logs/components/chart.tsx (1 hunks)
  • apps/dashboard/app/(app)/logs/components/filters/components/response-status.tsx (1 hunks)
  • apps/dashboard/app/(app)/logs/components/log-details/components/log-footer.tsx (1 hunks)
  • apps/dashboard/app/(app)/logs/components/logs-table-loading-row.tsx (1 hunks)
  • apps/dashboard/app/(app)/logs/components/logs-table.tsx (1 hunks)
  • apps/dashboard/app/(app)/logs/logs-page.tsx (1 hunks)
  • apps/dashboard/app/(app)/logs/page.tsx (1 hunks)
  • apps/dashboard/app/(app)/logs/query-state.ts (1 hunks)
  • apps/dashboard/components/timestamp-info.tsx (1 hunks)
  • apps/dashboard/styles/tailwind/base.css (3 hunks)
  • apps/dashboard/styles/tailwind/typography.css (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/dashboard/app/(app)/logs/components/log-details/components/log-footer.tsx
  • apps/dashboard/app/(app)/logs/logs-page.tsx
  • apps/dashboard/components/timestamp-info.tsx
  • apps/dashboard/app/(app)/logs/components/chart.tsx
🧰 Additional context used
📓 Learnings (1)
apps/dashboard/app/(app)/logs/components/logs-table.tsx (1)
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2143
File: apps/dashboard/app/(app)/logs/logs-page.tsx:77-83
Timestamp: 2024-12-03T14:17:08.016Z
Learning: The `<LogsTable />` component already implements virtualization to handle large datasets efficiently.
🔇 Additional comments (14)
apps/dashboard/styles/tailwind/base.css (1)

66-71: LGTM! Dark mode colors are well-balanced.

The dark mode chart colors maintain good visibility while preserving the semantic relationship with their light mode counterparts.

apps/dashboard/styles/tailwind/typography.css (1)

149-149: LGTM! Backtick is more appropriate for code snippets.

The change from single quote to backtick is more semantically correct for code snippets.

apps/dashboard/app/(app)/logs/query-state.ts (3)

33-34: Consider timezone handling for date ranges.

Using Date.now() directly might lead to timezone-related issues. Consider using a more robust date handling approach.


37-41: Add time range validation.

The current implementation doesn't validate if startTime is before endTime.


10-10: Verify usage of PickKeys type.

The PickKeys utility type appears to be unused in the current file.

apps/dashboard/app/(app)/logs/components/filters/components/response-status.tsx (4)

1-13: LGTM! Clean imports and well-defined interface.

The imports are appropriate and the CheckboxItemProps interface is well-typed with clear property names.


25-35: Fix state update race condition in handleItemChange

The function uses stale state when updating search params.


50-69: Enhance accessibility for the filter popover

The popover needs proper ARIA attributes and keyboard navigation.


73-86: LGTM! Well-structured checkbox component.

The CheckboxItem component follows good practices:

  • Proper HTML semantics with labels
  • Good accessibility support
  • Clear visual hierarchy
apps/dashboard/app/(app)/logs/page.tsx (5)

1-12: LGTM: Clean initialization with proper imports

The imports are well-organized and the searchParamsCache initialization follows the nuqs library pattern correctly.


14-19: Remove unused params type annotation

The params property is defined in the type annotation but not used in the function.


48-50: Avoid exposing internal error details to users

The error message still includes internal details that should not be exposed to users.


52-52: LGTM: Clean component rendering

The LogsPage component is rendered with appropriate props for initial data and workspace identification.


36-46: Verify log fetching parameters handling

The log fetching includes multiple optional parameters. Let's verify the parameter handling in the clickhouse API.

@chronark
Copy link
Collaborator

chronark commented Dec 4, 2024

CleanShot 2024-12-04 at 16 33 25@2x
the separators are sometimes full width and sometimes not

@chronark
Copy link
Collaborator

chronark commented Dec 4, 2024

CleanShot 2024-12-04 at 16 34 15@2x
meta field has weird corners, probably missing an overflow-hidden

Copy link
Collaborator

@chronark chronark left a comment

Choose a reason for hiding this comment

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

CleanShot 2024-12-04 at 16 43 11@2x

We should not parse the body into key-values, and more importantly we should't uppercase their keys, it loses the information that this was actually json, and while we always expect json, the user might mess up and send us something invalid.

I'm good with optimistically calling JSON.stringify(JSON.parse(body), null, 2) in a try catch to try and format it nicely but falling back to displaying the raw string

apps/dashboard/app/(app)/logs/components/logs-table.tsx Outdated Show resolved Hide resolved
apps/dashboard/app/(app)/logs/components/logs-table.tsx Outdated Show resolved Hide resolved
@ogzhanolguncu
Copy link
Contributor Author

CleanShot 2024-12-04 at 16 43 11@2x

We should not parse the body into key-values, and more importantly we should't uppercase their keys, it loses the information that this was actually json, and while we always expect json, the user might mess up and send us something invalid.

I'm good with optimistically calling JSON.stringify(JSON.parse(body), null, 2) in a try catch to try and format it nicely but falling back to displaying the raw string

I agree, yeah. Let's do this.

@ogzhanolguncu
Copy link
Contributor Author

CleanShot 2024-12-04 at 16 33 25@2x the separators are sometimes full width and sometimes not

Those are actually padded from left on purpose, but we can change that if we are not happy.

@chronark
Copy link
Collaborator

chronark commented Dec 4, 2024

CleanShot 2024-12-04 at 16 33 25@2x the separators are sometimes full width and sometimes not

Those are actually padded from left on purpose, but we can change that if we are not happy.

It looks a bit off and unintentional, but maybe that's just me
It's a nitpick really and might change with vitors design anyways

let's keep it as is for now to move quickly

This PR also adds escape listener when you navigating table with
keyboard
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (2)
apps/dashboard/app/(app)/logs/components/log-details/components/log-section.tsx (2)

3-9: Extract props interface for better maintainability.

Consider extracting the props interface to improve code organization and reusability.

+interface LogSectionProps {
+  details: string | string[];
+  title: string;
+}
+
-export const LogSection = ({
-  details,
-  title,
-}: {
-  details: string | string[];
-  title: string;
-}) => {
+export const LogSection = ({ details, title }: LogSectionProps) => {

11-14: Consider using design tokens for measurements.

Replace hardcoded pixel values with design system tokens for better maintainability and consistency.

-<div className="px-3 flex flex-col gap-[2px]">
-  <span className="text-sm text-content/65 font-sans">{title}</span>
-  <Card className="rounded-[5px] bg-background-subtle">
-    <CardContent className="p-2 text-[12px]">
+<div className="px-3 flex flex-col gap-1">
+  <span className="text-sm text-content/65 font-sans">{title}</span>
+  <Card className="rounded-md bg-background-subtle">
+    <CardContent className="p-2 text-xs">
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 9d10310 and c97487b.

📒 Files selected for processing (6)
  • apps/dashboard/app/(app)/logs/components/log-details/components/log-footer.tsx (1 hunks)
  • apps/dashboard/app/(app)/logs/components/log-details/components/log-meta.tsx (1 hunks)
  • apps/dashboard/app/(app)/logs/components/log-details/components/log-section.tsx (1 hunks)
  • apps/dashboard/app/(app)/logs/components/log-details/index.tsx (1 hunks)
  • apps/dashboard/app/(app)/logs/components/logs-table.tsx (1 hunks)
  • apps/dashboard/app/(app)/logs/utils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/dashboard/app/(app)/logs/components/log-details/components/log-meta.tsx
  • apps/dashboard/app/(app)/logs/components/log-details/index.tsx
  • apps/dashboard/app/(app)/logs/components/logs-table.tsx
  • apps/dashboard/app/(app)/logs/components/log-details/components/log-footer.tsx
🔇 Additional comments (4)
apps/dashboard/app/(app)/logs/components/log-details/components/log-section.tsx (1)

18-19: ⚠️ Potential issue

Improve header parsing logic.

The current splitting logic might break for headers containing multiple colons.

-const [key, ...valueParts] = header.split(":");
-const value = valueParts.join(":").trim();
+const colonIndex = header.indexOf(':');
+const key = colonIndex > -1 ? header.slice(0, colonIndex) : header;
+const value = colonIndex > -1 ? header.slice(colonIndex + 1).trim() : '';
apps/dashboard/app/(app)/logs/utils.ts (3)

1-11: LGTM! Well-structured error handling implementation.

The custom error class follows TypeScript best practices with proper type safety and immutable context.


13-48: ⚠️ Potential issue

Add input validation and structured logging

Several security and robustness improvements are needed:

 export const extractResponseField = <K extends keyof ResponseBody>(
   log: Log,
   fieldName: K,
 ): ResponseBody[K] | null => {
+  // Validate fieldName
+  if (typeof fieldName !== 'string' && typeof fieldName !== 'number' && typeof fieldName !== 'symbol') {
+    console.error('[extractResponseField] Invalid fieldName type', { fieldName });
+    return null;
+  }
+
   if (!log?.response_body) {
-    console.error("Invalid log or missing response_body");
+    console.error("[extractResponseField] Invalid log or missing response_body", { logId: log?.request_id });
     return null;
   }

+  // Validate response body size
+  const MAX_RESPONSE_BODY_SIZE = 1024 * 1024; // 1MB
+  if (log.response_body.length > MAX_RESPONSE_BODY_SIZE) {
+    console.error("[extractResponseField] Response body exceeds size limit", {
+      logId: log.request_id,
+      size: log.response_body.length
+    });
+    return null;
+  }

   try {
     const parsedBody = JSON.parse(log.response_body) as ResponseBody;

1-84: Verify usage patterns and error handling consistency

Let's verify how these utilities are used across the codebase to ensure consistent error handling patterns.

✅ Verification successful

Consistent error handling and usage patterns verified

Based on the search results, I can confirm:

  1. The utility functions are used consistently across the logs feature:

    • extractResponseField is used in log details components for parsing meta, code, and permissions
    • getRequestHeader is used for extracting user-agent information
    • safeParseJson is used for safely displaying request and response bodies
  2. Error handling patterns are consistent:

    • All functions properly handle edge cases with appropriate error logging
    • Error messages are descriptive and include relevant context
    • Console logging follows a consistent pattern across the codebase
  3. The implementation aligns with the codebase's error handling practices:

    • Uses console.error for critical failures
    • Uses console.warn for non-critical issues
    • Includes contextual information in error messages
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check usage patterns of the utility functions

# Check for usage of extractResponseField
echo "Checking extractResponseField usage:"
rg "extractResponseField" -A 2

# Check for usage of getRequestHeader
echo "Checking getRequestHeader usage:"
rg "getRequestHeader" -A 2

# Check for usage of safeParseJson
echo "Checking safeParseJson usage:"
rg "safeParseJson" -A 2

# Check for consistent error logging patterns
echo "Checking error logging patterns:"
rg "console\.(error|warn)" --type ts

Length of output: 15622

@chronark chronark merged commit 0e42754 into main Dec 5, 2024
26 checks passed
@chronark chronark deleted the logs-page-v2 branch December 5, 2024 11:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants