/* This file is based on the open source project https://google.github.io/sqlcommenter/ */ import { Version } from "../conf/version"; import reqtrack from "./index"; import { traceParent } from "./tracecontext"; /* eslint-disable @typescript-eslint/no-unused-vars */ const routeKey = "route"; const controllerKey = "controller"; const actionKey = "action"; const frameworkKey = "framework"; const driverKey = "driver"; const traceparentKey = "traceparent"; const tracestateKey = "tracestate"; const applicationKey = "application"; /* eslint-enable @typescript-eslint/no-unused-vars */ /** * Adds a tracing comment to a query, which can then be used to trace the query * by the sidecar, and if supported by the database server, traced by the * database server too. * * (CloudSQL supports this, but not sure about other databases) */ export function queryWithComment(query: string, driver?: string): string { const ctx = reqtrack.current(); if (ctx === undefined) { // If we're not tracking the request, return the query unmodified return query; } const tags: Record = { [frameworkKey]: `encore/js/${Version}`, [traceparentKey]: traceParent(ctx.traceID, ctx.spanID), }; if (driver !== undefined) { tags[driverKey] = driver; } const comment = `/*${convertMapToComment(tags)}*/`; // A semicolon at the end of the SQL statement means the query ends there. // We need to insert the comment before that to be considered as part of the SQL statemtent. const semicolonIndex = query.lastIndexOf(";"); if (semicolonIndex !== -1) { // Check there's only whitespace after the semicolon const whitespace = query.substring(semicolonIndex + 1); if (!/^\s*$/.test(whitespace)) { return query + comment; } return ( query.substring(0, semicolonIndex) + comment + query.substring(semicolonIndex) ); } else { return query + comment; } } /** * ConvertMapToComment returns a comment string given a map of key-value pairs of tags. * * There are few steps involved here: * - Sorting the tags by key string * - url encoding the key value pairs * - Formatting the key value pairs as "key1=value1,key2=value2" format. */ function convertMapToComment(tags: Record): string { const keys = Object.keys(tags).sort(); const pairs = keys.map( (key) => `${encodeURIComponent(key)}='${encodeURIComponent(tags[key]).replace( "'", "\\'", )}'`, ); return pairs.join(","); }