diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 785211a..3f77fa6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -157,54 +157,77 @@ jobs: # Publish in dependency order: types -> core -> all others - name: Publish @logtide/types + continue-on-error: true working-directory: packages/types run: pnpm publish --no-git-checks --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }} - name: Publish @logtide/core + continue-on-error: true working-directory: packages/core run: pnpm publish --no-git-checks --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }} - name: Publish @logtide/sdk-node + continue-on-error: true working-directory: packages/node run: pnpm publish --no-git-checks --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }} + - name: Publish @logtide/express + continue-on-error: true + working-directory: packages/express + run: pnpm publish --no-git-checks --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }} + + - name: Publish @logtide/fastify + continue-on-error: true + working-directory: packages/fastify + run: pnpm publish --no-git-checks --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }} + - name: Publish @logtide/nextjs + continue-on-error: true working-directory: packages/nextjs run: pnpm publish --no-git-checks --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }} - name: Publish @logtide/nuxt + continue-on-error: true working-directory: packages/nuxt run: pnpm publish --no-git-checks --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }} - name: Publish @logtide/sveltekit + continue-on-error: true working-directory: packages/sveltekit run: pnpm publish --no-git-checks --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }} - name: Publish @logtide/hono + continue-on-error: true working-directory: packages/hono run: pnpm publish --no-git-checks --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }} - name: Publish @logtide/angular + continue-on-error: true working-directory: packages/angular run: pnpm publish --no-git-checks --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }} - name: Publish @logtide/elysia + continue-on-error: true working-directory: packages/elysia run: pnpm publish --no-git-checks --access public env: @@ -222,6 +245,8 @@ jobs: echo "| @logtide/types | \`npm i @logtide/types@${VERSION}\` |" >> $GITHUB_STEP_SUMMARY echo "| @logtide/core | \`npm i @logtide/core@${VERSION}\` |" >> $GITHUB_STEP_SUMMARY echo "| @logtide/sdk-node | \`npm i @logtide/sdk-node@${VERSION}\` |" >> $GITHUB_STEP_SUMMARY + echo "| @logtide/express | \`npm i @logtide/express@${VERSION}\` |" >> $GITHUB_STEP_SUMMARY + echo "| @logtide/fastify | \`npm i @logtide/fastify@${VERSION}\` |" >> $GITHUB_STEP_SUMMARY echo "| @logtide/nextjs | \`npm i @logtide/nextjs@${VERSION}\` |" >> $GITHUB_STEP_SUMMARY echo "| @logtide/nuxt | \`npm i @logtide/nuxt@${VERSION}\` |" >> $GITHUB_STEP_SUMMARY echo "| @logtide/sveltekit | \`npm i @logtide/sveltekit@${VERSION}\` |" >> $GITHUB_STEP_SUMMARY diff --git a/CHANGELOG.md b/CHANGELOG.md index c695236..43fb355 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.6] - 2026-02-08 + +### Changed + +#### DSN Simplified +- Removed redundant `projectId` from DSN format — the API key already embeds the project ID +- New DSN format: `https://lp_APIKEY@host` (legacy format with path still accepted for backward compatibility) +- Added `apiUrl` + `apiKey` as alternative to DSN string (backward compatible with `@logtide/sdk-node` config format) +- Added `resolveDSN()` helper that accepts either `dsn` or `apiUrl` + `apiKey` +- Removed `projectId` field from `DSN` interface (`@logtide/types`) +- Removed `X-Project-Id` header from `LogtideHttpTransport` and `OtlpHttpTransport` (`@logtide/core`) + +#### Dynamic Service Name +- `service` in `ClientOptions` is now **optional** — each framework package defaults to its own name (`'express'`, `'fastify'`, `'hono'`, `'elysia'`, `'nextjs'`, `'sveltekit'`, `'nuxt'`, `'angular'`) +- Added `service?: string` field and `setService()` method to `Scope` — allows overriding service name per-request or per-module +- Service resolution chain: `scope.service` → `options.service` → framework default → `'unknown'` + +#### Mock Server +- Removed `X-Project-Id` from CORS headers and request tracking + +#### Documentation +- Updated DSN format examples across all package READMEs + ## [0.5.5] - 2026-02-07 ### Added @@ -77,4 +100,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Root README with package table, architecture diagram, development guide - Branch protection documentation (`.github/BRANCH_PROTECTION.md`) +[0.5.6]: https://github.com/logtide-dev/logtide-javascript/releases/tag/v0.5.6 [0.5.5]: https://github.com/logtide-dev/logtide-javascript/releases/tag/v0.5.5 diff --git a/README.md b/README.md index 1349a04..1a7e992 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,14 @@ npm install @logtide/elysia # Elysia ```typescript // Every integration follows the same pattern: { - dsn: 'https://lp_your_key@your-logtide-instance.com/project-id', + dsn: 'https://lp_your_key@your-logtide-instance.com', + service: 'my-app', +} + +// Or use apiUrl + apiKey separately: +{ + apiUrl: 'https://your-logtide-instance.com', + apiKey: 'lp_your_key', service: 'my-app', } ``` diff --git a/package.json b/package.json index 0d8aead..eb9c908 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "0.5.5", + "version": "0.5.6", "scripts": { "build": "pnpm -r --filter @logtide/* build", "test": "pnpm -r --filter @logtide/* test", diff --git a/packages/angular/README.md b/packages/angular/README.md index 1327aae..bad6cfe 100644 --- a/packages/angular/README.md +++ b/packages/angular/README.md @@ -52,7 +52,10 @@ export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(withInterceptorsFromDi()), provideLogtide({ - dsn: 'https://lp_your_key@your-instance.com/project-id', + dsn: 'https://lp_your_key@your-instance.com', + // Or use apiUrl + apiKey instead of dsn: + // apiUrl: 'https://your-instance.com', + // apiKey: 'lp_your_key', service: 'my-angular-app', environment: 'production', }), @@ -72,7 +75,8 @@ import { getLogtideProviders } from '@logtide/angular'; imports: [HttpClientModule], providers: [ ...getLogtideProviders({ - dsn: 'https://lp_your_key@your-instance.com/project-id', + dsn: 'https://lp_your_key@your-instance.com', + // Or: apiUrl + apiKey instead of dsn service: 'my-angular-app', }), ], diff --git a/packages/angular/package.json b/packages/angular/package.json index b51e1a0..3820129 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -1,6 +1,6 @@ { "name": "@logtide/angular", - "version": "0.5.5", + "version": "0.5.6", "description": "LogTide SDK integration for Angular — ErrorHandler, HTTP Interceptor, trace propagation", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/angular/src/provide.ts b/packages/angular/src/provide.ts index 01cd2e1..78b4f4f 100644 --- a/packages/angular/src/provide.ts +++ b/packages/angular/src/provide.ts @@ -33,6 +33,7 @@ export function provideLogtide(options: ClientOptions): EnvironmentProviders { useFactory: () => { return () => { hub.init({ + service: 'angular', ...options, integrations: [ new GlobalErrorIntegration(), @@ -72,6 +73,7 @@ export function getLogtideProviders(options: ClientOptions): Provider[] { useFactory: () => { return () => { hub.init({ + service: 'angular', ...options, integrations: [ new GlobalErrorIntegration(), diff --git a/packages/core/README.md b/packages/core/README.md index dd42b75..bad173d 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -49,7 +49,10 @@ import { hub } from '@logtide/core'; // Initialize once hub.init({ - dsn: 'https://lp_your_key@your-instance.com/project-id', + dsn: 'https://lp_your_key@your-instance.com', + // Or use apiUrl + apiKey instead of dsn: + // apiUrl: 'https://your-instance.com', + // apiKey: 'lp_your_key', service: 'my-app', }); @@ -81,7 +84,10 @@ process.on('SIGTERM', () => hub.close()); import { LogtideClient } from '@logtide/core'; const client = new LogtideClient({ - dsn: 'https://lp_your_key@your-instance.com/project-id', + dsn: 'https://lp_your_key@your-instance.com', + // Or use apiUrl + apiKey instead of dsn: + // apiUrl: 'https://your-instance.com', + // apiKey: 'lp_your_key', service: 'my-app', }); diff --git a/packages/core/package.json b/packages/core/package.json index 1591560..06beb10 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@logtide/core", - "version": "0.5.5", + "version": "0.5.6", "description": "Core client, hub, scope, transports, and utilities for the LogTide SDK", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 3d44ebd..efe6f26 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -8,7 +8,7 @@ import type { Span, Transport, } from '@logtide/types'; -import { parseDSN } from './dsn'; +import { resolveDSN } from './dsn'; import { Scope } from './scope'; import { SpanManager, type StartSpanOptions } from './span-manager'; import { BreadcrumbBuffer } from './breadcrumb-buffer'; @@ -26,7 +26,7 @@ class DefaultTransport implements Transport { private spanTransport: BatchTransport; constructor(options: ClientOptions) { - const dsn = parseDSN(options.dsn); + const dsn = resolveDSN(options); this.logTransport = new BatchTransport({ inner: new LogtideHttpTransport(dsn), @@ -41,7 +41,7 @@ class DefaultTransport implements Transport { }); this.spanTransport = new BatchTransport({ - inner: new OtlpHttpTransport(dsn, options.service), + inner: new OtlpHttpTransport(dsn, options.service || 'unknown'), batchSize: options.batchSize, flushInterval: options.flushInterval, maxBufferSize: options.maxBufferSize, @@ -103,7 +103,7 @@ export class LogtideClient implements IClient { return this._isInitialized; } - get service(): string { + get service(): string | undefined { return this.options.service; } @@ -115,6 +115,10 @@ export class LogtideClient implements IClient { return this.options.release; } + private resolveService(scope?: Scope): string { + return scope?.service || this.options.service || 'unknown'; + } + // ─── Logging ─────────────────────────────────────────── captureLog( @@ -124,7 +128,7 @@ export class LogtideClient implements IClient { scope?: Scope, ): void { const entry: InternalLogEntry = { - service: this.options.service, + service: this.resolveService(scope), level: level as LogLevel, message, time: new Date().toISOString(), diff --git a/packages/core/src/dsn.ts b/packages/core/src/dsn.ts index fec484a..430e996 100644 --- a/packages/core/src/dsn.ts +++ b/packages/core/src/dsn.ts @@ -1,24 +1,21 @@ -import type { DSN } from '@logtide/types'; +import type { ClientOptions, DSN } from '@logtide/types'; /** * Parse a LogTide DSN string into its components. - * Format: https://lp_APIKEY@host/PROJECT_ID + * Format: https://lp_APIKEY@host + * Legacy format with path (https://lp_APIKEY@host/PROJECT_ID) is also accepted; the path is ignored. */ export function parseDSN(dsn: string): DSN { try { const url = new URL(dsn); const apiKey = url.username; - const projectId = url.pathname.replace(/^\//, ''); const apiUrl = `${url.protocol}//${url.host}`; if (!apiKey) { throw new Error('Missing API key in DSN'); } - if (!projectId) { - throw new Error('Missing project ID in DSN'); - } - return { apiUrl, apiKey, projectId }; + return { apiUrl, apiKey }; } catch (err) { if (err instanceof Error && err.message.startsWith('Missing')) { throw err; @@ -26,3 +23,20 @@ export function parseDSN(dsn: string): DSN { throw new Error(`Invalid DSN: ${dsn}`); } } + +/** + * Resolve a DSN from ClientOptions. + * Accepts either a `dsn` string or separate `apiUrl` + `apiKey` fields. + */ +export function resolveDSN(options: ClientOptions): DSN { + if (options.dsn) { + return parseDSN(options.dsn); + } + if (options.apiUrl && options.apiKey) { + return { + apiUrl: options.apiUrl.replace(/\/$/, ''), + apiKey: options.apiKey, + }; + } + throw new Error('Either "dsn" or both "apiUrl" and "apiKey" must be provided'); +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 3232db6..ed1ce83 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -23,7 +23,7 @@ export { SpanManager, type StartSpanOptions } from './span-manager'; export { BreadcrumbBuffer } from './breadcrumb-buffer'; // DSN -export { parseDSN } from './dsn'; +export { parseDSN, resolveDSN } from './dsn'; // Transports export { LogtideHttpTransport } from './transport/logtide-http'; diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts index 8fcea94..2ff5e65 100644 --- a/packages/core/src/scope.ts +++ b/packages/core/src/scope.ts @@ -5,6 +5,7 @@ import { BreadcrumbBuffer } from './breadcrumb-buffer'; export class Scope { traceId: string; spanId?: string; + service?: string; tags: Record = {}; extras: Record = {}; private breadcrumbs: BreadcrumbBuffer; @@ -15,6 +16,11 @@ export class Scope { this.breadcrumbs = new BreadcrumbBuffer(maxBreadcrumbs); } + setService(service: string): this { + this.service = service; + return this; + } + setTag(key: string, value: string): this { this.tags[key] = value; return this; @@ -51,6 +57,7 @@ export class Scope { clone(): Scope { const scope = new Scope(this.traceId); scope.spanId = this.spanId; + scope.service = this.service; scope.tags = { ...this.tags }; scope.extras = { ...this.extras }; for (const bc of this.breadcrumbs.getAll()) { diff --git a/packages/core/src/transport/logtide-http.ts b/packages/core/src/transport/logtide-http.ts index e73f435..3dcac2a 100644 --- a/packages/core/src/transport/logtide-http.ts +++ b/packages/core/src/transport/logtide-http.ts @@ -15,7 +15,6 @@ export class LogtideHttpTransport implements Transport { headers: { 'Content-Type': 'application/json', 'X-API-Key': this.dsn.apiKey, - 'X-Project-Id': this.dsn.projectId, }, body: JSON.stringify({ logs }), }); diff --git a/packages/core/src/transport/otlp-http.ts b/packages/core/src/transport/otlp-http.ts index 57e1cfa..973ee70 100644 --- a/packages/core/src/transport/otlp-http.ts +++ b/packages/core/src/transport/otlp-http.ts @@ -69,7 +69,6 @@ export class OtlpHttpTransport implements Transport { headers: { 'Content-Type': 'application/json', 'X-API-Key': this.dsn.apiKey, - 'X-Project-Id': this.dsn.projectId, }, body: JSON.stringify(payload), }); diff --git a/packages/core/tests/dsn.test.ts b/packages/core/tests/dsn.test.ts index 3b72b6a..b96c202 100644 --- a/packages/core/tests/dsn.test.ts +++ b/packages/core/tests/dsn.test.ts @@ -1,40 +1,49 @@ import { describe, it, expect } from 'vitest'; -import { parseDSN } from '../src/dsn'; +import { parseDSN, resolveDSN } from '../src/dsn'; describe('parseDSN', () => { - it('should parse a valid DSN', () => { + it('should parse a DSN without path (new format)', () => { + const result = parseDSN('https://lp_abc123@api.logtide.dev'); + expect(result).toEqual({ + apiUrl: 'https://api.logtide.dev', + apiKey: 'lp_abc123', + }); + }); + + it('should accept legacy DSN with project ID path and ignore it', () => { const result = parseDSN('https://lp_abc123@api.logtide.dev/my-project'); expect(result).toEqual({ apiUrl: 'https://api.logtide.dev', apiKey: 'lp_abc123', - projectId: 'my-project', }); }); it('should handle DSN with port', () => { - const result = parseDSN('https://lp_key@localhost:3000/proj1'); + const result = parseDSN('https://lp_key@localhost:3000'); expect(result).toEqual({ apiUrl: 'https://localhost:3000', apiKey: 'lp_key', - projectId: 'proj1', }); }); it('should handle http scheme', () => { - const result = parseDSN('http://lp_key@localhost/project'); + const result = parseDSN('http://lp_key@localhost'); expect(result).toEqual({ apiUrl: 'http://localhost', apiKey: 'lp_key', - projectId: 'project', }); }); - it('should throw on missing API key', () => { - expect(() => parseDSN('https://api.logtide.dev/project')).toThrow('Missing API key'); + it('should handle DSN with trailing slash', () => { + const result = parseDSN('https://lp_key@api.logtide.dev/'); + expect(result).toEqual({ + apiUrl: 'https://api.logtide.dev', + apiKey: 'lp_key', + }); }); - it('should throw on missing project ID', () => { - expect(() => parseDSN('https://lp_key@api.logtide.dev/')).toThrow('Missing project ID'); + it('should throw on missing API key', () => { + expect(() => parseDSN('https://api.logtide.dev/project')).toThrow('Missing API key'); }); it('should throw on invalid DSN string', () => { @@ -45,3 +54,57 @@ describe('parseDSN', () => { expect(() => parseDSN('')).toThrow('Invalid DSN'); }); }); + +describe('resolveDSN', () => { + it('should resolve from dsn string', () => { + const result = resolveDSN({ dsn: 'https://lp_key@api.logtide.dev' }); + expect(result).toEqual({ + apiUrl: 'https://api.logtide.dev', + apiKey: 'lp_key', + }); + }); + + it('should resolve from apiUrl + apiKey', () => { + const result = resolveDSN({ apiUrl: 'http://localhost:8080', apiKey: 'lp_test_key' }); + expect(result).toEqual({ + apiUrl: 'http://localhost:8080', + apiKey: 'lp_test_key', + }); + }); + + it('should strip trailing slash from apiUrl', () => { + const result = resolveDSN({ apiUrl: 'http://localhost:8080/', apiKey: 'lp_key' }); + expect(result).toEqual({ + apiUrl: 'http://localhost:8080', + apiKey: 'lp_key', + }); + }); + + it('should prefer dsn over apiUrl + apiKey when both provided', () => { + const result = resolveDSN({ + dsn: 'https://lp_dsn_key@api.logtide.dev', + apiUrl: 'http://localhost:8080', + apiKey: 'lp_other_key', + }); + expect(result).toEqual({ + apiUrl: 'https://api.logtide.dev', + apiKey: 'lp_dsn_key', + }); + }); + + it('should throw when neither dsn nor apiUrl+apiKey provided', () => { + expect(() => resolveDSN({})).toThrow('Either "dsn" or both "apiUrl" and "apiKey" must be provided'); + }); + + it('should throw when only apiUrl provided without apiKey', () => { + expect(() => resolveDSN({ apiUrl: 'http://localhost:8080' })).toThrow( + 'Either "dsn" or both "apiUrl" and "apiKey" must be provided', + ); + }); + + it('should throw when only apiKey provided without apiUrl', () => { + expect(() => resolveDSN({ apiKey: 'lp_key' })).toThrow( + 'Either "dsn" or both "apiUrl" and "apiKey" must be provided', + ); + }); +}); diff --git a/packages/elysia/README.md b/packages/elysia/README.md index e7354cd..0e45fab 100644 --- a/packages/elysia/README.md +++ b/packages/elysia/README.md @@ -47,7 +47,10 @@ import { logtide } from '@logtide/elysia'; const app = new Elysia() .use(logtide({ - dsn: 'https://lp_your_key@your-instance.com/project-id', + dsn: 'https://lp_your_key@your-instance.com', + // Or use apiUrl + apiKey instead of dsn: + // apiUrl: 'https://your-instance.com', + // apiKey: 'lp_your_key', service: 'my-elysia-api', environment: 'production', })) diff --git a/packages/elysia/package.json b/packages/elysia/package.json index bb0b149..f0c61f4 100644 --- a/packages/elysia/package.json +++ b/packages/elysia/package.json @@ -1,6 +1,6 @@ { "name": "@logtide/elysia", - "version": "0.5.5", + "version": "0.5.6", "description": "LogTide SDK plugin for Elysia — request tracing and error capture via lifecycle hooks", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/elysia/src/plugin.ts b/packages/elysia/src/plugin.ts index ae70dcb..42f6cd7 100644 --- a/packages/elysia/src/plugin.ts +++ b/packages/elysia/src/plugin.ts @@ -27,6 +27,7 @@ export interface LogtideElysiaOptions extends ClientOptions {} */ export function logtide(options: LogtideElysiaOptions) { hub.init({ + service: 'elysia', ...options, integrations: [ new ConsoleIntegration(), diff --git a/packages/express/README.md b/packages/express/README.md index 4e94457..b0ee878 100644 --- a/packages/express/README.md +++ b/packages/express/README.md @@ -48,7 +48,10 @@ import { logtide } from '@logtide/express'; const app = express(); app.use(logtide({ - dsn: 'https://lp_your_key@your-instance.com/project-id', + dsn: 'https://lp_your_key@your-instance.com', + // Or use apiUrl + apiKey instead of dsn: + // apiUrl: 'https://your-instance.com', + // apiKey: 'lp_your_key', service: 'my-express-api', environment: 'production', })); diff --git a/packages/express/package.json b/packages/express/package.json index ec36df5..814895a 100644 --- a/packages/express/package.json +++ b/packages/express/package.json @@ -1,6 +1,6 @@ { "name": "@logtide/express", - "version": "0.5.5", + "version": "0.5.6", "description": "LogTide SDK middleware for Express — request tracing and error capture", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/express/src/middleware.ts b/packages/express/src/middleware.ts index e26a108..f32bfc0 100644 --- a/packages/express/src/middleware.ts +++ b/packages/express/src/middleware.ts @@ -35,6 +35,7 @@ declare global { */ export function logtide(options: LogtideExpressOptions) { hub.init({ + service: 'express', ...options, integrations: [ new ConsoleIntegration(), diff --git a/packages/fastify/README.md b/packages/fastify/README.md index 1f3a509..4dd339c 100644 --- a/packages/fastify/README.md +++ b/packages/fastify/README.md @@ -48,7 +48,10 @@ import { logtide } from '@logtide/fastify'; const app = Fastify(); await app.register(logtide, { - dsn: 'https://lp_your_key@your-instance.com/project-id', + dsn: 'https://lp_your_key@your-instance.com', + // Or use apiUrl + apiKey instead of dsn: + // apiUrl: 'https://your-instance.com', + // apiKey: 'lp_your_key', service: 'my-fastify-api', environment: 'production', }); diff --git a/packages/fastify/package.json b/packages/fastify/package.json index 39b9ed6..4729653 100644 --- a/packages/fastify/package.json +++ b/packages/fastify/package.json @@ -1,6 +1,6 @@ { "name": "@logtide/fastify", - "version": "0.5.5", + "version": "0.5.6", "description": "LogTide SDK plugin for Fastify — request tracing and error capture", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/fastify/src/middleware.ts b/packages/fastify/src/middleware.ts index 7f97936..97147f8 100644 --- a/packages/fastify/src/middleware.ts +++ b/packages/fastify/src/middleware.ts @@ -35,6 +35,7 @@ declare module 'fastify' { export const logtide = fp( (fastify: FastifyInstance, options: LogtideFastifyOptions, done: (err?: Error) => void) => { hub.init({ + service: 'fastify', ...options, integrations: [ new ConsoleIntegration(), diff --git a/packages/hono/README.md b/packages/hono/README.md index 52e02a8..b9bfc31 100644 --- a/packages/hono/README.md +++ b/packages/hono/README.md @@ -48,7 +48,10 @@ import { logtide } from '@logtide/hono'; const app = new Hono(); app.use('*', logtide({ - dsn: 'https://lp_your_key@your-instance.com/project-id', + dsn: 'https://lp_your_key@your-instance.com', + // Or use apiUrl + apiKey instead of dsn: + // apiUrl: 'https://your-instance.com', + // apiKey: 'lp_your_key', service: 'my-hono-api', environment: 'production', })); diff --git a/packages/hono/package.json b/packages/hono/package.json index 80ba470..d8d9318 100644 --- a/packages/hono/package.json +++ b/packages/hono/package.json @@ -1,6 +1,6 @@ { "name": "@logtide/hono", - "version": "0.5.5", + "version": "0.5.6", "description": "LogTide SDK middleware for Hono — request tracing and error capture", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/hono/src/middleware.ts b/packages/hono/src/middleware.ts index 6f80cae..99f88a8 100644 --- a/packages/hono/src/middleware.ts +++ b/packages/hono/src/middleware.ts @@ -26,6 +26,7 @@ export interface LogtideHonoOptions extends ClientOptions {} */ export function logtide(options: LogtideHonoOptions) { hub.init({ + service: 'hono', ...options, integrations: [ new ConsoleIntegration(), diff --git a/packages/nextjs/README.md b/packages/nextjs/README.md index 646a048..10e7329 100644 --- a/packages/nextjs/README.md +++ b/packages/nextjs/README.md @@ -51,7 +51,10 @@ import { registerLogtide, captureRequestError } from '@logtide/nextjs/server'; export async function register() { await registerLogtide({ - dsn: 'https://lp_your_key@your-instance.com/project-id', + dsn: 'https://lp_your_key@your-instance.com', + // Or use apiUrl + apiKey instead of dsn: + // apiUrl: 'https://your-instance.com', + // apiKey: 'lp_your_key', service: 'my-nextjs-app', environment: process.env.NODE_ENV, }); @@ -72,7 +75,10 @@ import { useEffect } from 'react'; import { initLogtide, trackNavigation } from '@logtide/nextjs/client'; initLogtide({ - dsn: 'https://lp_your_key@your-instance.com/project-id', + dsn: 'https://lp_your_key@your-instance.com', + // Or use apiUrl + apiKey instead of dsn: + // apiUrl: 'https://your-instance.com', + // apiKey: 'lp_your_key', service: 'my-nextjs-app', }); @@ -99,7 +105,7 @@ Initialize LogTide in Next.js `instrumentation.ts`. Automatically installs `Cons import { registerLogtide } from '@logtide/nextjs/server'; await registerLogtide({ - dsn: 'https://lp_key@host/project', + dsn: 'https://lp_key@host', service: 'my-app', environment: 'production', release: '1.0.0', @@ -156,7 +162,7 @@ Initialize LogTide on the client side. Installs `GlobalErrorIntegration` for `un import { initLogtide } from '@logtide/nextjs/client'; initLogtide({ - dsn: 'https://lp_key@host/project', + dsn: 'https://lp_key@host', service: 'my-app', }); ``` diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 989e60d..7e0017f 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@logtide/nextjs", - "version": "0.5.5", + "version": "0.5.6", "description": "LogTide SDK integration for Next.js — auto error capture, request tracing, and performance spans", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/nextjs/src/client/index.ts b/packages/nextjs/src/client/index.ts index 491f9bd..0f7eaea 100644 --- a/packages/nextjs/src/client/index.ts +++ b/packages/nextjs/src/client/index.ts @@ -17,6 +17,7 @@ export { trackNavigation } from './navigation'; */ export function initLogtide(options: ClientOptions): void { hub.init({ + service: 'nextjs', ...options, integrations: [ new GlobalErrorIntegration(), diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts index 5e0e7ff..1ff8179 100644 --- a/packages/nextjs/src/server/index.ts +++ b/packages/nextjs/src/server/index.ts @@ -21,6 +21,7 @@ export { instrumentRequest, finishRequest } from './request-handler'; */ export async function registerLogtide(options: ClientOptions): Promise { hub.init({ + service: 'nextjs', ...options, integrations: [ new ConsoleIntegration(), diff --git a/packages/nuxt/README.md b/packages/nuxt/README.md index cc69e9b..d32ab31 100644 --- a/packages/nuxt/README.md +++ b/packages/nuxt/README.md @@ -49,7 +49,10 @@ export default defineNuxtConfig({ modules: ['@logtide/nuxt'], logtide: { - dsn: 'https://lp_your_key@your-instance.com/project-id', + dsn: 'https://lp_your_key@your-instance.com', + // Or use apiUrl + apiKey instead of dsn: + // apiUrl: 'https://your-instance.com', + // apiKey: 'lp_your_key', service: 'my-nuxt-app', environment: 'production', release: '1.0.0', diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 018097c..1e1ef73 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -1,6 +1,6 @@ { "name": "@logtide/nuxt", - "version": "0.5.5", + "version": "0.5.6", "description": "LogTide SDK integration for Nuxt — auto error capture, request tracing via Nitro hooks", "type": "module", "main": "./dist/module.cjs", diff --git a/packages/nuxt/src/module.ts b/packages/nuxt/src/module.ts index 03268bf..b54d476 100644 --- a/packages/nuxt/src/module.ts +++ b/packages/nuxt/src/module.ts @@ -11,7 +11,7 @@ export default defineNuxtModule({ }, defaults: { dsn: '', - service: 'nuxt-app', + service: 'nuxt', }, setup(options, nuxt) { if (!options.dsn) { diff --git a/packages/nuxt/src/runtime/client-plugin.ts b/packages/nuxt/src/runtime/client-plugin.ts index 102db37..7a0fce2 100644 --- a/packages/nuxt/src/runtime/client-plugin.ts +++ b/packages/nuxt/src/runtime/client-plugin.ts @@ -7,7 +7,7 @@ import { defineNuxtPlugin, useRuntimeConfig } from '#app'; export default defineNuxtPlugin((nuxtApp) => { const config = useRuntimeConfig().public.logtide as { dsn: string; - service: string; + service?: string; environment?: string; release?: string; debug?: boolean; @@ -17,7 +17,7 @@ export default defineNuxtPlugin((nuxtApp) => { hub.init({ dsn: config.dsn, - service: config.service, + service: config.service ?? 'nuxt', environment: config.environment, release: config.release, debug: config.debug, diff --git a/packages/nuxt/src/runtime/server-plugin.ts b/packages/nuxt/src/runtime/server-plugin.ts index da48d5a..5dd9e50 100644 --- a/packages/nuxt/src/runtime/server-plugin.ts +++ b/packages/nuxt/src/runtime/server-plugin.ts @@ -13,7 +13,7 @@ import { defineNitroPlugin, getRequestURL, getRequestHeaders } from 'h3'; export default defineNitroPlugin((nitroApp) => { const config = useRuntimeConfig().logtide as { dsn: string; - service: string; + service?: string; environment?: string; release?: string; debug?: boolean; @@ -23,7 +23,7 @@ export default defineNitroPlugin((nitroApp) => { hub.init({ dsn: config.dsn, - service: config.service, + service: config.service ?? 'nuxt', environment: config.environment, release: config.release, debug: config.debug, diff --git a/packages/sveltekit/README.md b/packages/sveltekit/README.md index 4640338..655f1e9 100644 --- a/packages/sveltekit/README.md +++ b/packages/sveltekit/README.md @@ -48,7 +48,10 @@ yarn add @logtide/sveltekit import { logtideHandle, logtideHandleError, logtideHandleFetch } from '@logtide/sveltekit/server'; export const handle = logtideHandle({ - dsn: 'https://lp_your_key@your-instance.com/project-id', + dsn: 'https://lp_your_key@your-instance.com', + // Or use apiUrl + apiKey instead of dsn: + // apiUrl: 'https://your-instance.com', + // apiKey: 'lp_your_key', service: 'my-sveltekit-app', environment: 'production', }); @@ -65,7 +68,8 @@ export const handleFetch = logtideHandleFetch(); import { initLogtide } from '@logtide/sveltekit/client'; initLogtide({ - dsn: 'https://lp_your_key@your-instance.com/project-id', + dsn: 'https://lp_your_key@your-instance.com', + // Or: apiUrl + apiKey instead of dsn service: 'my-sveltekit-app', }); ``` diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json index 611eb3c..0b5dc4f 100644 --- a/packages/sveltekit/package.json +++ b/packages/sveltekit/package.json @@ -1,6 +1,6 @@ { "name": "@logtide/sveltekit", - "version": "0.5.5", + "version": "0.5.6", "description": "LogTide SDK integration for SvelteKit — handle, handleError, handleFetch hooks", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index 1c8c79b..f8e0a08 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -48,6 +48,7 @@ interface HandleFetchInput { */ export function logtideHandle(options: ClientOptions) { hub.init({ + service: 'sveltekit', ...options, integrations: [ new ConsoleIntegration(), diff --git a/packages/types/README.md b/packages/types/README.md index edee688..3b2a5db 100644 --- a/packages/types/README.md +++ b/packages/types/README.md @@ -87,8 +87,8 @@ import type { ClientOptions, DSN } from '@logtide/types'; | Type | Description | |------|-------------| -| `ClientOptions` | Full configuration: DSN, service, batching, retry, circuit breaker, etc. | -| `DSN` | Parsed DSN with `apiUrl`, `apiKey`, `projectId` | +| `ClientOptions` | Full configuration: DSN (or `apiUrl` + `apiKey`), service, batching, retry, circuit breaker, etc. | +| `DSN` | Parsed DSN with `apiUrl`, `apiKey` | --- diff --git a/packages/types/package.json b/packages/types/package.json index 9d4e6aa..64ea3da 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@logtide/types", - "version": "0.5.5", + "version": "0.5.6", "description": "Shared type definitions for the LogTide SDK ecosystem", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/types/src/client-options.ts b/packages/types/src/client-options.ts index 6276c2e..c14c698 100644 --- a/packages/types/src/client-options.ts +++ b/packages/types/src/client-options.ts @@ -4,14 +4,17 @@ import type { Transport } from './transport'; export interface DSN { apiUrl: string; apiKey: string; - projectId: string; } export interface ClientOptions { - /** DSN string: https://lp_APIKEY@api.logtide.dev/PROJECT_ID */ - dsn: string; - /** Service name for log attribution */ - service: string; + /** DSN string: https://lp_APIKEY@api.logtide.dev */ + dsn?: string; + /** API base URL (alternative to DSN, e.g. 'http://localhost:8080') */ + apiUrl?: string; + /** API key (alternative to DSN, e.g. 'lp_your_api_key_here') */ + apiKey?: string; + /** Service name for log attribution (optional, defaults to framework name) */ + service?: string; /** Environment (e.g. production, staging) */ environment?: string; /** Release / version identifier */ diff --git a/test-apps/mock-server/src/server.ts b/test-apps/mock-server/src/server.ts index 969676e..aecad90 100644 --- a/test-apps/mock-server/src/server.ts +++ b/test-apps/mock-server/src/server.ts @@ -30,7 +30,6 @@ export interface MockServerState { requests: Array<{ timestamp: number; apiKey: string | null; - projectId: string | null; endpoint: string; count: number; }>; @@ -59,7 +58,7 @@ export function createMockServer() { // CORS res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-API-Key, X-Project-Id'); + res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-API-Key'); if (method === 'OPTIONS') { res.writeHead(204); @@ -69,7 +68,6 @@ export function createMockServer() { try { const apiKey = req.headers['x-api-key'] as string | undefined; - const projectId = req.headers['x-project-id'] as string | undefined; // Ingest endpoint - receives logs from SDK if (url.pathname === '/api/v1/ingest' && method === 'POST') { @@ -81,7 +79,6 @@ export function createMockServer() { state.requests.push({ timestamp: Date.now(), apiKey: apiKey ?? null, - projectId: projectId ?? null, endpoint: '/api/v1/ingest', count: logs.length, }); @@ -118,7 +115,6 @@ export function createMockServer() { state.requests.push({ timestamp: Date.now(), apiKey: apiKey ?? null, - projectId: projectId ?? null, endpoint: '/v1/otlp/traces', count: spans.length, });