Skip to content

Commit 0a65741

Browse files
committed
automate 'Available Log Structures' bullet point list
1 parent bf5c4f9 commit 0a65741

File tree

5 files changed

+129
-63
lines changed

5 files changed

+129
-63
lines changed

.cspell/project-words.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ Sidekiq
3838
solargraph
3939
ssns
4040
testtask
41+
vercel
4142
wordlists
4243
xorshift
4344
yardoc

site/app/docs/type-safety/page.tsx

Lines changed: 4 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { CodeBlock } from '@/components/code-block';
22
import { EditPageLink } from '@/components/edit-page-link';
3+
import { LogStructuresList } from '@/components/log-structures-list';
34
import { RubyCodeExample } from '@/components/ruby-code-example';
45

5-
export default function TypeSafetyPage() {
6+
export default async function TypeSafetyPage() {
67
return (
78
<div className="space-y-6">
89
<h1 className="text-4xl font-bold">Type Safety</h1>
@@ -33,68 +34,8 @@ export default function TypeSafetyPage() {
3334
LogStruct provides several typed log structures for different use cases:
3435
</p>
3536

36-
<ul className="list-disc list-inside space-y-2 text-neutral-600 dark:text-neutral-400">
37-
<li>
38-
<code className="px-1 py-0.5 bg-neutral-100 dark:bg-neutral-800 rounded">
39-
LogStruct::Log::Plain
40-
</code>{' '}
41-
- For general purpose logging with a message and context
42-
</li>
43-
<li>
44-
<code className="px-1 py-0.5 bg-neutral-100 dark:bg-neutral-800 rounded">
45-
LogStruct::Log::Request
46-
</code>{' '}
47-
- For HTTP request details
48-
</li>
49-
<li>
50-
<code className="px-1 py-0.5 bg-neutral-100 dark:bg-neutral-800 rounded">
51-
LogStruct::Log::Error
52-
</code>{' '}
53-
- For exception details with stack traces
54-
</li>
55-
<li>
56-
<code className="px-1 py-0.5 bg-neutral-100 dark:bg-neutral-800 rounded">
57-
LogStruct::Log::ActionMailer
58-
</code>{' '}
59-
- For email delivery events
60-
</li>
61-
<li>
62-
<code className="px-1 py-0.5 bg-neutral-100 dark:bg-neutral-800 rounded">
63-
LogStruct::Log::ActiveJob
64-
</code>{' '}
65-
- For background job execution
66-
</li>
67-
<li>
68-
<code className="px-1 py-0.5 bg-neutral-100 dark:bg-neutral-800 rounded">
69-
LogStruct::Log::ActiveStorage
70-
</code>{' '}
71-
- For file storage operations
72-
</li>
73-
<li>
74-
<code className="px-1 py-0.5 bg-neutral-100 dark:bg-neutral-800 rounded">
75-
LogStruct::Log::Shrine
76-
</code>{' '}
77-
- For Shrine file upload events
78-
</li>
79-
<li>
80-
<code className="px-1 py-0.5 bg-neutral-100 dark:bg-neutral-800 rounded">
81-
LogStruct::Log::CarrierWave
82-
</code>{' '}
83-
- For CarrierWave upload events
84-
</li>
85-
<li>
86-
<code className="px-1 py-0.5 bg-neutral-100 dark:bg-neutral-800 rounded">
87-
LogStruct::Log::Sidekiq
88-
</code>{' '}
89-
- For Sidekiq job processing
90-
</li>
91-
<li>
92-
<code className="px-1 py-0.5 bg-neutral-100 dark:bg-neutral-800 rounded">
93-
LogStruct::Log::Security
94-
</code>{' '}
95-
- For security-related events
96-
</li>
97-
</ul>
37+
{/* Dynamic list of log structures from the JSON data */}
38+
<LogStructuresList />
9839

9940
<h2 className="text-2xl font-bold mt-10 mb-4">Enums and Constants</h2>
10041
<p className="text-neutral-600 dark:text-neutral-400 mb-4">
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { getLogStructureDescription } from '@/lib/log-structure-descriptions';
2+
import { cache } from 'react';
3+
4+
// Interface for the structure in the log structs JSON file
5+
interface LogStructsData {
6+
[key: string]: {
7+
name: string;
8+
fields: Record<string, unknown>;
9+
};
10+
}
11+
12+
// Function to get log structs data from JSON file with caching
13+
const getLogStructsData = cache(async (): Promise<LogStructsData> => {
14+
try {
15+
// Use dynamic import to load the JSON file
16+
const data = await import('@/lib/log-generation/sorbet-log-structs.json');
17+
return data.default as LogStructsData;
18+
} catch (error) {
19+
console.error('Error loading log structures data:', error);
20+
return {};
21+
}
22+
});
23+
24+
/**
25+
* Component that displays a bullet point list of LogStruct log structures
26+
* with their descriptions
27+
*/
28+
export async function LogStructuresList() {
29+
// Get the log structs data
30+
const structsData = await getLogStructsData();
31+
32+
// Extract struct names (sorted alphabetically)
33+
const structEntries = Object.entries(structsData)
34+
.sort(([, a], [, b]) => a.name.localeCompare(b.name));
35+
36+
return (
37+
<ul className="list-disc list-inside space-y-2 text-neutral-600 dark:text-neutral-400">
38+
{structEntries.map(([fullName, { name }]) => (
39+
<li key={fullName}>
40+
<code className="px-1 py-0.5 bg-neutral-100 dark:bg-neutral-800 rounded">
41+
LogStruct::Log::{name}
42+
</code>{' '}
43+
- {getLogStructureDescription(name)}
44+
</li>
45+
))}
46+
</ul>
47+
);
48+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { describe, it, expect } from '@jest/globals';
2+
import {
3+
getLogStructureDescription,
4+
LOG_STRUCTURE_DESCRIPTIONS,
5+
} from '../log-structure-descriptions';
6+
7+
describe('Log Structure Descriptions', () => {
8+
it('has descriptions for all common log structures', () => {
9+
// Test that we have descriptions for the core log structures
10+
const expectedStructures = [
11+
'Plain',
12+
'Request',
13+
'Error',
14+
'ActiveJob',
15+
'ActionMailer',
16+
'ActiveStorage',
17+
'Shrine',
18+
'CarrierWave',
19+
'Sidekiq',
20+
'Security',
21+
];
22+
23+
// Check that all expected structures have descriptions
24+
expectedStructures.forEach((struct) => {
25+
expect(LOG_STRUCTURE_DESCRIPTIONS).toHaveProperty(struct);
26+
expect(LOG_STRUCTURE_DESCRIPTIONS[struct]).toBeTruthy();
27+
});
28+
});
29+
30+
it('getLogStructureDescription returns the correct description', () => {
31+
// Test with a known structure
32+
expect(getLogStructureDescription('Plain')).toBe(
33+
'For general purpose logging',
34+
);
35+
expect(getLogStructureDescription('Request')).toBe(
36+
'For HTTP request details',
37+
);
38+
});
39+
40+
it('getLogStructureDescription throws an error for unknown structures', () => {
41+
// Test with an unknown structure
42+
expect(() => getLogStructureDescription('NonExistentStructure')).toThrow(
43+
'No description found for log structure: NonExistentStructure',
44+
);
45+
});
46+
});
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Descriptions for each LogStruct log structure
3+
* These are used in the documentation to explain what each log structure is for
4+
*/
5+
export const LOG_STRUCTURE_DESCRIPTIONS: Record<string, string> = {
6+
Plain: 'For general purpose logging',
7+
Request: 'For HTTP request details',
8+
Error: 'For exception details with stack traces',
9+
ActionMailer: 'For email delivery events',
10+
ActiveJob: 'For background job execution',
11+
ActiveStorage: 'For file storage operations',
12+
Shrine: 'For Shrine file upload events',
13+
CarrierWave: 'For CarrierWave upload events',
14+
Sidekiq: 'For Sidekiq job processing',
15+
Security: 'For security-related events',
16+
};
17+
18+
/**
19+
* Get the description for a log structure
20+
* @param structName The name of the log structure (e.g., "Plain", "Request")
21+
* @returns The description of the log structure
22+
* @throws Error if no description is found for the structure
23+
*/
24+
export function getLogStructureDescription(structName: string): string {
25+
const description = LOG_STRUCTURE_DESCRIPTIONS[structName];
26+
if (!description) {
27+
throw new Error(`No description found for log structure: ${structName}`);
28+
}
29+
return description;
30+
}

0 commit comments

Comments
 (0)