Merge branch 'master' into chore/update-db-delete
commit
3898947284
|
|
@ -43,6 +43,7 @@ tmp
|
|||
# User context files for AI assistant tools
|
||||
.context/*
|
||||
!.context/README.md
|
||||
.task.md
|
||||
|
||||
# External repos
|
||||
.ext/*
|
||||
|
|
|
|||
|
|
@ -832,7 +832,92 @@ Sets the endpoint of an S3-compatible, HTTP/2-enabled object store cache.
|
|||
|
||||
#### log-filter
|
||||
|
||||
Sets the filter directive for logs.
|
||||
Sets the filter directive for logs. Use this option to control the verbosity of
|
||||
server logs globally or for specific components.
|
||||
|
||||
##### Log levels
|
||||
|
||||
The following log levels are available (from least to most verbose):
|
||||
|
||||
| Level | Description |
|
||||
| :------ | :---------------------------------------------------------------------------------------------------- |
|
||||
| `error` | Only errors |
|
||||
| `warn` | Warnings and errors |
|
||||
| `info` | Informational messages, warnings, and errors _(default)_ |
|
||||
| `debug` | Debug information for troubleshooting, plus all above levels |
|
||||
| `trace` | Very detailed tracing information, plus all above levels (produces high log volume) |
|
||||
|
||||
##### Basic usage
|
||||
|
||||
To set the log level globally, pass one of the log levels:
|
||||
|
||||
<!--pytest.mark.skip-->
|
||||
|
||||
```sh
|
||||
influxdb3 serve --log-filter debug
|
||||
```
|
||||
|
||||
##### Targeted filtering
|
||||
|
||||
Globally enabling `debug` or `trace` produces a high volume of log output.
|
||||
For more targeted debugging, you can set different log levels for specific
|
||||
components using the format `<global_level>,<component>=<level>`.
|
||||
|
||||
###### Debug write buffer operations
|
||||
|
||||
<!--pytest.mark.skip-->
|
||||
|
||||
```sh
|
||||
influxdb3 serve --log-filter info,influxdb3_write_buffer=debug
|
||||
```
|
||||
|
||||
###### Trace WAL operations
|
||||
|
||||
<!--pytest.mark.skip-->
|
||||
|
||||
```sh
|
||||
influxdb3 serve --log-filter info,influxdb3_wal=trace
|
||||
```
|
||||
|
||||
###### Multiple targeted filters
|
||||
|
||||
<!--pytest.mark.skip-->
|
||||
|
||||
```sh
|
||||
influxdb3 serve --log-filter info,influxdb3_write_buffer=debug,influxdb3_wal=debug
|
||||
```
|
||||
|
||||
{{% show-in "enterprise" %}}
|
||||
|
||||
###### Debug Enterprise storage engine operations
|
||||
|
||||
<!--pytest.mark.skip-->
|
||||
|
||||
```sh
|
||||
influxdb3 serve --log-filter info,influxdb3_pacha_tree=debug
|
||||
```
|
||||
|
||||
{{% /show-in %}}
|
||||
|
||||
##### Common component names
|
||||
|
||||
The following are common component names you can use for targeted filtering:
|
||||
|
||||
| Component | Description |
|
||||
| :------------------------------------ | :------------------------------------------------------- |
|
||||
| `influxdb3_write_buffer` | Write buffer operations |
|
||||
| `influxdb3_wal` | Write-ahead log operations |
|
||||
| `influxdb3_catalog` | Catalog and schema operations |
|
||||
| `influxdb3_cache` | Caching operations |
|
||||
{{% show-in "enterprise" %}}`influxdb3_pacha_tree` | Enterprise storage engine operations |
|
||||
`influxdb3_enterprise` | Enterprise-specific features |
|
||||
{{% /show-in %}}
|
||||
|
||||
> [!Note]
|
||||
> Targeted filtering requires knowledge of the codebase component names.
|
||||
> The component names correspond to Rust package names in the InfluxDB 3 source
|
||||
> code. Use `debug` or `trace` sparingly on specific components to avoid
|
||||
> excessive log output.
|
||||
|
||||
| influxdb3 serve option | Environment variable |
|
||||
| :--------------------- | :------------------- |
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ Learn how to avoid unexpected results and recover from errors when writing to
|
|||
- [Review HTTP status codes](#review-http-status-codes)
|
||||
- [Troubleshoot failures](#troubleshoot-failures)
|
||||
- [Troubleshoot rejected points](#troubleshoot-rejected-points)
|
||||
{{% show-in "core,enterprise" %}}- [Troubleshoot write performance issues](#troubleshoot-write-performance-issues){{% /show-in %}}
|
||||
{{% show-in "core,enterprise" %}}- [Troubleshoot write performance issues](#troubleshoot-write-performance-issues)
|
||||
- [Use debug logs for troubleshooting](#use-debug-logs-for-troubleshooting){{% /show-in %}}
|
||||
|
||||
## Handle write responses
|
||||
|
||||
|
|
@ -105,4 +106,28 @@ influxdb3 serve \
|
|||
Replace {{% code-placeholder-key %}}`PERCENTAGE`{{% /code-placeholder-key %}} with the percentage
|
||||
of available memory to allocate (for example, `35%` for write-heavy workloads).
|
||||
|
||||
### Use debug logs for troubleshooting
|
||||
|
||||
For deeper investigation of write issues, enable debug logging for specific
|
||||
components. Debug logs provide detailed information about write buffer
|
||||
operations and WAL activity.
|
||||
|
||||
To enable debug logs for write operations, restart {{% product-name %}} with
|
||||
targeted log filters:
|
||||
|
||||
<!--pytest.mark.skip-->
|
||||
|
||||
```sh
|
||||
influxdb3 serve --log-filter info,influxdb3_write_buffer=debug
|
||||
```
|
||||
|
||||
<!--pytest.mark.skip-->
|
||||
|
||||
```sh
|
||||
influxdb3 serve --log-filter info,influxdb3_wal=debug
|
||||
```
|
||||
|
||||
For more information about log levels and targeted filtering, see
|
||||
[log-filter configuration](/influxdb3/version/reference/config-options/#log-filter).
|
||||
|
||||
{{% /show-in %}}
|
||||
|
|
|
|||
|
|
@ -205,6 +205,25 @@ For other InfluxDB versions, see the [Support and feedback](#bug-reports-and-fee
|
|||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract style attributes from HTML comments and apply to headings.
|
||||
* Converts: `#### Heading <!-- {.class} -->` to `#### Heading {.class}`
|
||||
*
|
||||
* Supported class formats:
|
||||
* - {.green}, {.orange} - Color styling
|
||||
* - {.recommended}, {.not-recommended} - Semantic styling
|
||||
* - Any other {.classname} format
|
||||
*
|
||||
* This allows source READMEs to render cleanly on GitHub (which ignores
|
||||
* HTML comments) while still supporting Hugo style classes in docs-v2.
|
||||
*/
|
||||
function extractStyleAttributes(content) {
|
||||
// Match headings with HTML comment style attributes
|
||||
// Pattern: (#+) (heading text) <!-- ({.classname}) -->
|
||||
const pattern = /^(#{1,6})\s+(.+?)\s*<!--\s*(\{[^}]+\})\s*-->\s*$/gm;
|
||||
return content.replace(pattern, '$1 $2 $3');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure code blocks are properly formatted.
|
||||
*/
|
||||
|
|
@ -277,6 +296,7 @@ function transformContent(content, pluginName, config) {
|
|||
content = convertTomlReadmeLinks(content);
|
||||
content = addProductShortcodes(content);
|
||||
content = enhanceOpeningParagraph(content);
|
||||
content = extractStyleAttributes(content);
|
||||
content = fixCodeBlockFormatting(content);
|
||||
|
||||
// Add schema requirements if applicable
|
||||
|
|
|
|||
|
|
@ -45,9 +45,18 @@ const URL_PATTERN_MAP = {
|
|||
|
||||
const PRODUCT_NAME_MAP = {
|
||||
influxdb3_core: { name: 'InfluxDB 3 Core', version: 'core' },
|
||||
influxdb3_enterprise: { name: 'InfluxDB 3 Enterprise', version: 'enterprise' },
|
||||
influxdb3_cloud_dedicated: { name: 'InfluxDB Cloud Dedicated', version: 'cloud-dedicated' },
|
||||
influxdb3_cloud_serverless: { name: 'InfluxDB Cloud Serverless', version: 'cloud-serverless' },
|
||||
influxdb3_enterprise: {
|
||||
name: 'InfluxDB 3 Enterprise',
|
||||
version: 'enterprise',
|
||||
},
|
||||
influxdb3_cloud_dedicated: {
|
||||
name: 'InfluxDB Cloud Dedicated',
|
||||
version: 'cloud-dedicated',
|
||||
},
|
||||
influxdb3_cloud_serverless: {
|
||||
name: 'InfluxDB Cloud Serverless',
|
||||
version: 'cloud-serverless',
|
||||
},
|
||||
influxdb3_clustered: { name: 'InfluxDB Clustered', version: 'clustered' },
|
||||
influxdb3_explorer: { name: 'InfluxDB 3 Explorer', version: 'explorer' },
|
||||
influxdb_cloud: { name: 'InfluxDB Cloud (TSM)', version: 'cloud' },
|
||||
|
|
@ -81,12 +90,18 @@ function detectBaseUrl() {
|
|||
}
|
||||
|
||||
// Check if Hugo dev server is running on localhost
|
||||
if (process.env.HUGO_ENV === 'development' || process.env.NODE_ENV === 'development') {
|
||||
if (
|
||||
process.env.HUGO_ENV === 'development' ||
|
||||
process.env.NODE_ENV === 'development'
|
||||
) {
|
||||
return 'http://localhost:1313';
|
||||
}
|
||||
|
||||
// Check for staging environment
|
||||
if (process.env.HUGO_ENV === 'staging' || process.env.DEPLOY_ENV === 'staging') {
|
||||
if (
|
||||
process.env.HUGO_ENV === 'staging' ||
|
||||
process.env.DEPLOY_ENV === 'staging'
|
||||
) {
|
||||
return process.env.STAGING_URL || 'https://test2.docs.influxdata.com';
|
||||
}
|
||||
|
||||
|
|
@ -277,6 +292,79 @@ function createTurndownService() {
|
|||
},
|
||||
});
|
||||
|
||||
// Handle tabbed content - associate tab names with their content
|
||||
// This rule handles both .tabs-wrapper and .code-tabs-wrapper containers
|
||||
turndownService.addRule('tabbedContent', {
|
||||
filter: function (node) {
|
||||
return (
|
||||
node.nodeName === 'DIV' &&
|
||||
node.classList &&
|
||||
(node.classList.contains('tabs-wrapper') ||
|
||||
node.classList.contains('code-tabs-wrapper'))
|
||||
);
|
||||
},
|
||||
replacement: function (_content, node, options) {
|
||||
const isCodeTabs = node.classList.contains('code-tabs-wrapper');
|
||||
const tabsSelector = isCodeTabs ? '.code-tabs' : '.tabs';
|
||||
const contentSelector = isCodeTabs ? '.code-tab-content' : '.tab-content';
|
||||
|
||||
// Extract tab names from anchor elements
|
||||
const tabsContainer = node.querySelector(tabsSelector);
|
||||
const tabLinks = tabsContainer
|
||||
? Array.from(tabsContainer.querySelectorAll('a'))
|
||||
: [];
|
||||
const tabNames = tabLinks.map((link) => link.textContent.trim());
|
||||
|
||||
// Extract tab content sections
|
||||
// Note: :scope selector is not supported in Turndown's DOM parser,
|
||||
// so we use a simple selector and filter by parent
|
||||
const allContentSections = Array.from(
|
||||
node.querySelectorAll(contentSelector)
|
||||
);
|
||||
// Filter to only direct children of the tabs-wrapper
|
||||
const contentSections = allContentSections.filter(
|
||||
(section) => section.parentNode === node
|
||||
);
|
||||
|
||||
// If no tabs or content found, fall back to default conversion
|
||||
if (tabNames.length === 0 || contentSections.length === 0) {
|
||||
return _content;
|
||||
}
|
||||
|
||||
// Build markdown with explicit tab-content association
|
||||
// Use bold labels instead of headings to avoid breaking heading hierarchy
|
||||
const parts = [];
|
||||
|
||||
// Add a comment indicating this is tabbed content
|
||||
parts.push(
|
||||
'\n<!-- Tabbed content: Select one of the following options -->\n'
|
||||
);
|
||||
|
||||
// Process each tab and its content
|
||||
const maxTabs = Math.max(tabNames.length, contentSections.length);
|
||||
for (let i = 0; i < maxTabs; i++) {
|
||||
const tabName = tabNames[i] || `Tab ${i + 1}`;
|
||||
const contentSection = contentSections[i];
|
||||
|
||||
// Add tab label with bold formatting (not a heading to preserve hierarchy)
|
||||
parts.push(`\n**${tabName}:**\n`);
|
||||
|
||||
if (contentSection) {
|
||||
// Recursively convert the content inside the tab
|
||||
const innerHtml = contentSection.innerHTML;
|
||||
const innerContent = turndownService.turndown(innerHtml);
|
||||
parts.push(innerContent.trim());
|
||||
}
|
||||
|
||||
parts.push('\n');
|
||||
}
|
||||
|
||||
parts.push('\n<!-- End tabbed content -->\n');
|
||||
|
||||
return parts.join('\n');
|
||||
},
|
||||
});
|
||||
|
||||
// Remove navigation, footer, and other non-content elements
|
||||
turndownService.remove([
|
||||
'nav',
|
||||
|
|
@ -398,9 +486,9 @@ function generateFrontmatter(metadata, urlPath, baseUrl = '') {
|
|||
// Sanitize description (remove newlines, truncate to reasonable length)
|
||||
let description = metadata.description || '';
|
||||
description = description
|
||||
.replace(/\s+/g, ' ') // Replace all whitespace (including newlines) with single space
|
||||
.replace(/\s+/g, ' ') // Replace all whitespace (including newlines) with single space
|
||||
.trim()
|
||||
.substring(0, 500); // Truncate to 500 characters max
|
||||
.substring(0, 500); // Truncate to 500 characters max
|
||||
|
||||
// Add token estimate (rough: 4 chars per token)
|
||||
const contentLength = metadata.content?.length || 0;
|
||||
|
|
@ -414,7 +502,7 @@ function generateFrontmatter(metadata, urlPath, baseUrl = '') {
|
|||
title: metadata.title,
|
||||
description: description,
|
||||
url: fullUrl,
|
||||
estimated_tokens: estimatedTokens
|
||||
estimated_tokens: estimatedTokens,
|
||||
};
|
||||
|
||||
if (product) {
|
||||
|
|
@ -425,10 +513,16 @@ function generateFrontmatter(metadata, urlPath, baseUrl = '') {
|
|||
}
|
||||
|
||||
// Serialize to YAML (handles special characters properly)
|
||||
return '---\n' + yaml.dump(frontmatterObj, {
|
||||
lineWidth: -1, // Disable line wrapping
|
||||
noRefs: true // Disable anchors/aliases
|
||||
}).trim() + '\n---';
|
||||
return (
|
||||
'---\n' +
|
||||
yaml
|
||||
.dump(frontmatterObj, {
|
||||
lineWidth: -1, // Disable line wrapping
|
||||
noRefs: true, // Disable anchors/aliases
|
||||
})
|
||||
.trim() +
|
||||
'\n---'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -439,15 +533,20 @@ function generateFrontmatter(metadata, urlPath, baseUrl = '') {
|
|||
* @param {string} baseUrl - Base URL for full URL construction
|
||||
* @returns {string} YAML frontmatter as string
|
||||
*/
|
||||
function generateSectionFrontmatter(metadata, urlPath, childPages, baseUrl = '') {
|
||||
function generateSectionFrontmatter(
|
||||
metadata,
|
||||
urlPath,
|
||||
childPages,
|
||||
baseUrl = ''
|
||||
) {
|
||||
const product = detectProduct(urlPath);
|
||||
|
||||
// Sanitize description (remove newlines, truncate to reasonable length)
|
||||
let description = metadata.description || '';
|
||||
description = description
|
||||
.replace(/\s+/g, ' ') // Replace all whitespace (including newlines) with single space
|
||||
.replace(/\s+/g, ' ') // Replace all whitespace (including newlines) with single space
|
||||
.trim()
|
||||
.substring(0, 500); // Truncate to 500 characters max
|
||||
.substring(0, 500); // Truncate to 500 characters max
|
||||
|
||||
// Add token estimate (rough: 4 chars per token)
|
||||
const contentLength = metadata.content?.length || 0;
|
||||
|
|
@ -469,7 +568,7 @@ function generateSectionFrontmatter(metadata, urlPath, childPages, baseUrl = '')
|
|||
url: fullUrl,
|
||||
type: 'section',
|
||||
pages: childPages.length,
|
||||
estimated_tokens: estimatedTokens
|
||||
estimated_tokens: estimatedTokens,
|
||||
};
|
||||
|
||||
if (product) {
|
||||
|
|
@ -481,17 +580,23 @@ function generateSectionFrontmatter(metadata, urlPath, childPages, baseUrl = '')
|
|||
|
||||
// List child pages with full URLs
|
||||
if (childPages.length > 0) {
|
||||
frontmatterObj.child_pages = childPages.map(child => ({
|
||||
frontmatterObj.child_pages = childPages.map((child) => ({
|
||||
url: normalizedBaseUrl ? `${normalizedBaseUrl}${child.url}` : child.url,
|
||||
title: child.title
|
||||
title: child.title,
|
||||
}));
|
||||
}
|
||||
|
||||
// Serialize to YAML (handles special characters properly)
|
||||
return '---\n' + yaml.dump(frontmatterObj, {
|
||||
lineWidth: -1, // Disable line wrapping
|
||||
noRefs: true // Disable anchors/aliases
|
||||
}).trim() + '\n---';
|
||||
return (
|
||||
'---\n' +
|
||||
yaml
|
||||
.dump(frontmatterObj, {
|
||||
lineWidth: -1, // Disable line wrapping
|
||||
noRefs: true, // Disable anchors/aliases
|
||||
})
|
||||
.trim() +
|
||||
'\n---'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -506,7 +611,9 @@ async function convertToMarkdown(htmlContent, urlPath) {
|
|||
// Detect base URL for the environment
|
||||
const baseUrl = detectBaseUrl();
|
||||
if (DEBUG) {
|
||||
console.log(`[DEBUG] Base URL detected: ${baseUrl} (NODE_ENV=${process.env.NODE_ENV}, HUGO_ENV=${process.env.HUGO_ENV}, BASE_URL=${process.env.BASE_URL})`);
|
||||
console.log(
|
||||
`[DEBUG] Base URL detected: ${baseUrl} (NODE_ENV=${process.env.NODE_ENV}, HUGO_ENV=${process.env.HUGO_ENV}, BASE_URL=${process.env.BASE_URL})`
|
||||
);
|
||||
}
|
||||
|
||||
// Use Rust converter if available (10× faster)
|
||||
|
|
@ -514,7 +621,10 @@ async function convertToMarkdown(htmlContent, urlPath) {
|
|||
try {
|
||||
return rustConverter.convertToMarkdown(htmlContent, urlPath, baseUrl);
|
||||
} catch (err) {
|
||||
console.warn(`Rust conversion failed for ${urlPath}, falling back to JavaScript:`, err.message);
|
||||
console.warn(
|
||||
`Rust conversion failed for ${urlPath}, falling back to JavaScript:`,
|
||||
err.message
|
||||
);
|
||||
// Fall through to JavaScript implementation
|
||||
}
|
||||
}
|
||||
|
|
@ -563,9 +673,17 @@ async function convertSectionToMarkdown(
|
|||
// Use Rust converter if available (10× faster)
|
||||
if (USE_RUST && rustConverter) {
|
||||
try {
|
||||
return rustConverter.convertSectionToMarkdown(sectionHtml, sectionUrlPath, childHtmls, baseUrl);
|
||||
return rustConverter.convertSectionToMarkdown(
|
||||
sectionHtml,
|
||||
sectionUrlPath,
|
||||
childHtmls,
|
||||
baseUrl
|
||||
);
|
||||
} catch (err) {
|
||||
console.warn(`Rust section conversion failed for ${sectionUrlPath}, falling back to JavaScript:`, err.message);
|
||||
console.warn(
|
||||
`Rust section conversion failed for ${sectionUrlPath}, falling back to JavaScript:`,
|
||||
err.message
|
||||
);
|
||||
// Fall through to JavaScript implementation
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue