From 5dd75dc8263e80493674cd6db3d39d7a496ac234 Mon Sep 17 00:00:00 2001 From: Krash Date: Sun, 18 Oct 2026 01:50:14 +0000 Subject: [PATCH] fix(transports): replace leftover 'logitron' names and hardcoded library version DataDog and Segment payloads still shipped the legacy 'logitron' name to user accounts (service, logger.name, source_type_name, metric tags and context.library.name), and Segment's context.library.version was a hardcoded '1.0.0'. Switch the user-facing defaults to 'logixia' and source the version from a new src/version.ts. The Datadog metric name logitron.logs.* is kept as-is with a TODO(v2) note so existing dashboards keep resolving. Refs #35 --- .../__tests__/datadog.transport.test.ts | 57 +++++++++++++++++++ .../__tests__/segment.transport.test.ts | 47 +++++++++++++++ src/transports/datadog.transport.ts | 14 +++-- src/transports/segment.transport.ts | 37 ++++++------ src/types/http.types.ts | 2 +- src/types/index.ts | 2 +- src/version.ts | 8 +++ 7 files changed, 141 insertions(+), 26 deletions(-) create mode 100644 src/transports/__tests__/datadog.transport.test.ts create mode 100644 src/transports/__tests__/segment.transport.test.ts create mode 100644 src/version.ts diff --git a/src/transports/__tests__/datadog.transport.test.ts b/src/transports/__tests__/datadog.transport.test.ts new file mode 100644 index 0000000..c2bc43c --- /dev/null +++ b/src/transports/__tests__/datadog.transport.test.ts @@ -0,0 +1,57 @@ +/** + * Guards issue #35: the legacy 'logitron' name must not leak into the payloads + * DataDog receives. The default service, logger name, metric tags and + * source_type_name are all user-visible data, so a regression here silently + * mislabels every application's log stream. + * + * The payload builder is exercised directly on a prototype instance: the + * transport's own constructor kicks off an async initialize() that needs a live + * provider, which is not what this unit test is about. + */ +import type { TransportLogEntry } from '../../types/transport.types'; +import { DataDogTransport } from '../datadog.transport'; + +function makeEntry(): TransportLogEntry { + return { + timestamp: new Date('2026-01-01T00:00:00.000Z'), + level: 'info', + message: 'hello from the test suite', + }; +} + +interface DataDogLogPayload { + logs: Array<{ service: string; logger: { name: string } }>; +} + +interface DataDogInternals { + config: Record; + datadogConfig: { apiKey: string; service?: string; env?: string; version?: string }; + sendLogBatch: (entries: TransportLogEntry[]) => Promise; + makeLogsRequest: (endpoint: string, payload: unknown) => Promise; +} + +function makeTransport(): { transport: DataDogInternals; sent: unknown[] } { + const sent: unknown[] = []; + const transport = Object.create(DataDogTransport.prototype) as unknown as DataDogInternals; + transport.config = {}; + transport.datadogConfig = { apiKey: 'test-key', env: 'production' }; + transport.makeLogsRequest = (_endpoint, payload) => { + sent.push(payload); + return Promise.resolve(); + }; + return { transport, sent }; +} + +describe('DataDogTransport — outgoing payload branding', () => { + it('labels the log payload with the logixia service and logger, not logitron', async () => { + const { transport, sent } = makeTransport(); + + await transport.sendLogBatch([makeEntry()]); + + expect(sent).toHaveLength(1); + const payload = sent[0] as DataDogLogPayload; + expect(payload.logs[0]?.service).toBe('logixia'); + expect(payload.logs[0]?.logger.name).toBe('logixia'); + expect(JSON.stringify(payload)).not.toContain('logitron'); + }); +}); diff --git a/src/transports/__tests__/segment.transport.test.ts b/src/transports/__tests__/segment.transport.test.ts new file mode 100644 index 0000000..5a239ca --- /dev/null +++ b/src/transports/__tests__/segment.transport.test.ts @@ -0,0 +1,47 @@ +/** + * Guards issue #35: Segment payloads must carry the 'logixia' library name and + * the real package version, not the legacy 'logitron' name or a hardcoded + * '1.0.0'. `context.library` is user-visible data on every Segment event. + * + * The payload builder is exercised directly on a prototype instance: the + * transport's own constructor kicks off an async initialize() that needs a live + * provider, which is not what this unit test is about. + */ +import type { SegmentTransportConfig } from '../../types/transport.types'; +import { LOGIXIA_VERSION } from '../../version'; +import { SegmentTransport } from '../segment.transport'; + +interface SegmentLibraryPayload { + context: { library: { name: string; version: string } }; +} + +interface SegmentInternals { + segmentConfig: SegmentTransportConfig; + identify: (user: { id: string }) => Promise; + makeRequest: (endpoint: string, data: unknown) => Promise; +} + +function makeTransport(): { transport: SegmentInternals; sent: unknown[] } { + const sent: unknown[] = []; + const transport = Object.create(SegmentTransport.prototype) as unknown as SegmentInternals; + transport.segmentConfig = { writeKey: 'test-key' }; + transport.makeRequest = (_endpoint, data) => { + sent.push(data); + return Promise.resolve(); + }; + return { transport, sent }; +} + +describe('SegmentTransport — outgoing payload branding', () => { + it('stamps the logixia library name and the real package version', async () => { + const { transport, sent } = makeTransport(); + + await transport.identify({ id: 'user-1' }); + + expect(sent).toHaveLength(1); + const payload = sent[0] as SegmentLibraryPayload; + expect(payload.context.library.name).toBe('logixia'); + expect(payload.context.library.version).toBe(LOGIXIA_VERSION); + expect(JSON.stringify(payload)).not.toContain('logitron'); + }); +}); diff --git a/src/transports/datadog.transport.ts b/src/transports/datadog.transport.ts index 4f620d7..bb084eb 100644 --- a/src/transports/datadog.transport.ts +++ b/src/transports/datadog.transport.ts @@ -139,11 +139,11 @@ export class DataDogTransport extends AnalyticsTransport { timestamp: entry.timestamp.toISOString(), level: this.mapLogLevel(entry.level), message: entry.message, - service: this.datadogConfig.service || 'logitron', + service: this.datadogConfig.service || 'logixia', version: this.datadogConfig.version, env: this.datadogConfig.env || 'production', logger: { - name: 'logitron', + name: 'logixia', thread_name: 'main', }, ...transformed, @@ -157,13 +157,15 @@ export class DataDogTransport extends AnalyticsTransport { private transformToDataDogMetric(entry: TransportLogEntry): Record { return { + // TODO(v2): rename to logixia.logs.* — kept as logitron.logs.* so existing + // Datadog dashboards and monitors keep resolving. Maintainers decide when to break them. metric: `logitron.logs.${entry.level}`, points: [[Math.floor(entry.timestamp.getTime() / 1000), 1]], type: 'count', host: this.getHostname(), tags: [ `level:${entry.level}`, - `service:${this.datadogConfig.service || 'logitron'}`, + `service:${this.datadogConfig.service || 'logixia'}`, `env:${this.datadogConfig.env || 'production'}`, ...(entry.context ? [`context:${entry.context}`] : []), ...(entry.appName ? [`app:${entry.appName}`] : []), @@ -177,7 +179,7 @@ export class DataDogTransport extends AnalyticsTransport { span_id: this.generateSpanId(), parent_id: null, name: 'log.entry', - service: this.datadogConfig.service || 'logitron', + service: this.datadogConfig.service || 'logixia', resource: entry.message, type: 'log', start: entry.timestamp.getTime() * 1000000, // nanoseconds @@ -299,11 +301,11 @@ export class DataDogTransport extends AnalyticsTransport { date_happened: Math.floor(Date.now() / 1000), priority: 'normal', tags: [ - `service:${this.datadogConfig.service || 'logitron'}`, + `service:${this.datadogConfig.service || 'logixia'}`, `env:${this.datadogConfig.env || 'production'}`, ...tags, ], - source_type_name: 'logitron', + source_type_name: 'logixia', }; const url = `${this.baseUrl}/api/v1/events`; diff --git a/src/transports/segment.transport.ts b/src/transports/segment.transport.ts index 42cf260..5e7d21d 100644 --- a/src/transports/segment.transport.ts +++ b/src/transports/segment.transport.ts @@ -1,6 +1,7 @@ import type { SegmentTransportConfig, TransportLogEntry } from '../types/transport.types'; import { internalError } from '../utils/internal-log'; import { stringifyForLog } from '../utils/safe-stringify'; +import { LOGIXIA_VERSION } from '../version'; import type { AnalyticsUser } from './analytics.transport'; import { AnalyticsTransport } from './analytics.transport'; @@ -84,7 +85,7 @@ export class SegmentTransport extends AnalyticsTransport { context: { library: { name: 'logixia', - version: '1.0.0', + version: LOGIXIA_VERSION, }, app: { name: entry.appName || 'unknown', @@ -134,7 +135,7 @@ export class SegmentTransport extends AnalyticsTransport { headers: { 'Content-Type': 'application/json', Authorization: `Basic ${auth}`, - 'User-Agent': 'logixia/1.0.0', + 'User-Agent': `logixia/${LOGIXIA_VERSION}`, }, body: stringifyForLog(data), }); @@ -193,8 +194,8 @@ export class SegmentTransport extends AnalyticsTransport { }, context: { library: { - name: 'logitron', - version: '1.0.0', + name: 'logixia', + version: LOGIXIA_VERSION, }, }, }; @@ -215,8 +216,8 @@ export class SegmentTransport extends AnalyticsTransport { }, context: { library: { - name: 'logitron', - version: '1.0.0', + name: 'logixia', + version: LOGIXIA_VERSION, }, page: { url: properties.url || `/${name.toLowerCase().replace(/\s+/g, '-')}`, @@ -239,8 +240,8 @@ export class SegmentTransport extends AnalyticsTransport { }, context: { library: { - name: 'logitron', - version: '1.0.0', + name: 'logixia', + version: LOGIXIA_VERSION, }, screen: { name, @@ -265,8 +266,8 @@ export class SegmentTransport extends AnalyticsTransport { traits, context: { library: { - name: 'logitron', - version: '1.0.0', + name: 'logixia', + version: LOGIXIA_VERSION, }, }, }; @@ -282,8 +283,8 @@ export class SegmentTransport extends AnalyticsTransport { timestamp: new Date().toISOString(), context: { library: { - name: 'logitron', - version: '1.0.0', + name: 'logixia', + version: LOGIXIA_VERSION, }, }, }; @@ -305,8 +306,8 @@ export class SegmentTransport extends AnalyticsTransport { properties, context: { library: { - name: 'logitron', - version: '1.0.0', + name: 'logixia', + version: LOGIXIA_VERSION, }, }, }; @@ -334,8 +335,8 @@ export class SegmentTransport extends AnalyticsTransport { }, context: { library: { - name: 'logitron', - version: '1.0.0', + name: 'logixia', + version: LOGIXIA_VERSION, }, }, }; @@ -352,8 +353,8 @@ export class SegmentTransport extends AnalyticsTransport { properties: product, context: { library: { - name: 'logitron', - version: '1.0.0', + name: 'logixia', + version: LOGIXIA_VERSION, }, }, }; diff --git a/src/types/http.types.ts b/src/types/http.types.ts index 52ca57d..b8ad685 100644 --- a/src/types/http.types.ts +++ b/src/types/http.types.ts @@ -1,5 +1,5 @@ /** - * HTTP-related type definitions for Logitron + * HTTP-related type definitions for Logixia */ export interface HttpRequest { diff --git a/src/types/index.ts b/src/types/index.ts index 69f4d31..22eb01e 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,5 @@ /** - * Core type definitions for Logitron Logger + * Core type definitions for Logixia Logger */ import type { HttpRequest, HttpResponse } from './http.types'; diff --git a/src/version.ts b/src/version.ts new file mode 100644 index 0000000..00b40f4 --- /dev/null +++ b/src/version.ts @@ -0,0 +1,8 @@ +/** + * The published version of this package. + * + * Kept in sync with `version` in package.json by the release process. Importing + * from here (instead of reading package.json at runtime) keeps the built `dist` + * free of a JSON import that bundlers and consumers would have to resolve. + */ +export const LOGIXIA_VERSION = '1.12.0';