Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions src/transports/__tests__/datadog.transport.test.ts
Original file line number Diff line number Diff line change
@@ -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<string, unknown>;
datadogConfig: { apiKey: string; service?: string; env?: string; version?: string };
sendLogBatch: (entries: TransportLogEntry[]) => Promise<void>;
makeLogsRequest: (endpoint: string, payload: unknown) => Promise<void>;
}

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');
});
});
47 changes: 47 additions & 0 deletions src/transports/__tests__/segment.transport.test.ts
Original file line number Diff line number Diff line change
@@ -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<void>;
makeRequest: (endpoint: string, data: unknown) => Promise<void>;
}

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');
});
});
14 changes: 8 additions & 6 deletions src/transports/datadog.transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -157,13 +157,15 @@ export class DataDogTransport extends AnalyticsTransport {

private transformToDataDogMetric(entry: TransportLogEntry): Record<string, unknown> {
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}`] : []),
Expand All @@ -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
Expand Down Expand Up @@ -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`;
Expand Down
37 changes: 19 additions & 18 deletions src/transports/segment.transport.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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),
});
Expand Down Expand Up @@ -193,8 +194,8 @@ export class SegmentTransport extends AnalyticsTransport {
},
context: {
library: {
name: 'logitron',
version: '1.0.0',
name: 'logixia',
version: LOGIXIA_VERSION,
},
},
};
Expand All @@ -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, '-')}`,
Expand All @@ -239,8 +240,8 @@ export class SegmentTransport extends AnalyticsTransport {
},
context: {
library: {
name: 'logitron',
version: '1.0.0',
name: 'logixia',
version: LOGIXIA_VERSION,
},
screen: {
name,
Expand All @@ -265,8 +266,8 @@ export class SegmentTransport extends AnalyticsTransport {
traits,
context: {
library: {
name: 'logitron',
version: '1.0.0',
name: 'logixia',
version: LOGIXIA_VERSION,
},
},
};
Expand All @@ -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,
},
},
};
Expand All @@ -305,8 +306,8 @@ export class SegmentTransport extends AnalyticsTransport {
properties,
context: {
library: {
name: 'logitron',
version: '1.0.0',
name: 'logixia',
version: LOGIXIA_VERSION,
},
},
};
Expand Down Expand Up @@ -334,8 +335,8 @@ export class SegmentTransport extends AnalyticsTransport {
},
context: {
library: {
name: 'logitron',
version: '1.0.0',
name: 'logixia',
version: LOGIXIA_VERSION,
},
},
};
Expand All @@ -352,8 +353,8 @@ export class SegmentTransport extends AnalyticsTransport {
properties: product,
context: {
library: {
name: 'logitron',
version: '1.0.0',
name: 'logixia',
version: LOGIXIA_VERSION,
},
},
};
Expand Down
2 changes: 1 addition & 1 deletion src/types/http.types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* HTTP-related type definitions for Logitron
* HTTP-related type definitions for Logixia
*/

export interface HttpRequest {
Expand Down
2 changes: 1 addition & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Core type definitions for Logitron Logger
* Core type definitions for Logixia Logger
*/

import type { HttpRequest, HttpResponse } from './http.types';
Expand Down
8 changes: 8 additions & 0 deletions src/version.ts
Original file line number Diff line number Diff line change
@@ -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';
Loading