diff --git a/src/content-render/scripts/add-content-type.ts b/src/content-render/scripts/add-content-type.ts index f74d5e2177c3..88fc414a0a1f 100644 --- a/src/content-render/scripts/add-content-type.ts +++ b/src/content-render/scripts/add-content-type.ts @@ -102,13 +102,7 @@ function processFile(filePath: string, options: ScriptOptions) { // Remove the legacy type property if option is passed const removeLegacyType = Boolean(options.removeType && data.type) - // Skip if contentType already exists and we're not removing legacy type - if (data.contentType && !removeLegacyType) { - console.log(`contentType already set on ${relativePath}`) - return { processed: true, updated: false } - } - - const newContentType = data.contentType || determineContentType(relativePath, data.type || '') + const newContentType = determineContentType(relativePath, data.type || '') if (options.dryRun) { console.log(`\n${relativePath}`) @@ -121,9 +115,24 @@ function processFile(filePath: string, options: ScriptOptions) { return { processed: true, updated: false } } - // Set the contentType property if it doesn't exist - if (!data.contentType) { + // Check if we're actually changing an existing contentType + const isChangingContentType = data.contentType && data.contentType !== newContentType + const isAddingContentType = !data.contentType + + if (isChangingContentType) { + console.log( + `Changing contentType from '${data.contentType}' to '${newContentType}' on ${relativePath}`, + ) + } else if (isAddingContentType) { + console.log(`Adding contentType '${newContentType}' on ${relativePath}`) + } + + // Only update if there's actually a change needed + if (isChangingContentType || isAddingContentType) { data.contentType = newContentType + } else { + console.log(`contentType is already set to '${data.contentType}' on ${relativePath}`) + return { processed: true, updated: false } } let legacyTypeValue diff --git a/src/events/lib/hydro.ts b/src/events/lib/hydro.ts index 7a061ab2c4e5..34242d41c8e8 100644 --- a/src/events/lib/hydro.ts +++ b/src/events/lib/hydro.ts @@ -5,6 +5,9 @@ import { isNil } from 'lodash-es' import statsd from '@/observability/lib/statsd' import { report } from '@/observability/lib/failbot' import { MAX_REQUEST_TIMEOUT } from '@/frame/lib/constants' +import { createLogger } from '@/observability/logger' + +const logger = createLogger(import.meta.url) const TIME_OUT_TEXT = 'ms has passed since batch creation' const SERVER_DISCONNECT_TEXT = 'The server disconnected before a response was received' @@ -70,6 +73,13 @@ async function _publish( ) { const error = new Error(`Failed to send event to Hydro (${statusCode})`) if (inProd) { + logger.error('Failed to send event to Hydro', { + error, + statusCode, + body, + requestBody, + }) + // Report the error to Failbot report(error, { statusCode, body, requestBody }) } else { throw error diff --git a/src/search/lib/helpers/external-search-analytics.ts b/src/search/lib/helpers/external-search-analytics.ts index e2ba35b5241f..127304709c74 100644 --- a/src/search/lib/helpers/external-search-analytics.ts +++ b/src/search/lib/helpers/external-search-analytics.ts @@ -1,3 +1,4 @@ +import { ExtendedRequest } from '@/types' import { publish } from '@/events/lib/hydro' import { hydroNames } from '@/events/lib/schema' import { createLogger } from '@/observability/logger' @@ -9,7 +10,7 @@ const logger = createLogger(import.meta.url) * Returns null if the request should continue, or an error response object if validation failed */ export async function handleExternalSearchAnalytics( - req: any, + req: ExtendedRequest, searchContext: string, ): Promise<{ error: string; status: number } | null> { const host = req.headers['x-host'] || req.headers.host @@ -73,12 +74,12 @@ export async function handleExternalSearchAnalytics( version: '1.0.0', created: new Date().toISOString(), hostname: normalizedHost, - path: '', - search: '', + path: req?.context?.path || '', + search: 'REDACTED', hash: '', - path_language: 'en', - path_version: '', - path_product: '', + path_language: req?.context?.language || 'en', + path_version: req?.context?.version || '', + path_product: req?.context?.product || '', path_article: '', }, search_query: 'REDACTED', @@ -90,7 +91,7 @@ export async function handleExternalSearchAnalytics( await publish(analyticsPayload) } catch (error) { // Don't fail the request if analytics fails - console.error('Failed to send search analytics:', error) + logger.error('Failed to send search analytics:', { error }) } return null @@ -146,16 +147,12 @@ function stripPort(host: string): string { return hostname } -interface ExternalAPIRequestLike { - headers: Record -} - /** * Determines if a request is likely from an external API client rather than a browser * Uses multiple heuristics to detect programmatic vs browser requests */ const userAgentRegex = /^(curl|wget|python-requests|axios|node-fetch|Go-http-client|okhttp)/i -function isExternalAPIRequest(req: ExternalAPIRequestLike): boolean { +function isExternalAPIRequest(req: ExtendedRequest): boolean { const headers = req.headers // Browser security headers that APIs typically don't send