Skip to content

Commit

Permalink
fix plugin structure and BetterHttpInstrumentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Ankcorn committed Oct 19, 2023
1 parent 64ccbc7 commit 1fad416
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 55 deletions.
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
},
"homepage": "https://github.com/baselime/node-opentelemetry#readme",
"dependencies": {
"@opentelemetry/api": "^1.6.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.44.0",
"@opentelemetry/instrumentation": "^0.44.0",
"@opentelemetry/instrumentation-http": "^0.44.0",
Expand Down
20 changes: 20 additions & 0 deletions src/http-plugins/captureBody.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ClientRequest } from "http";

export function captureBody(request: ClientRequest): Promise<string> {
return new Promise((resolve, reject) => {
const chunks: string[] = [];
const oldWrite = request.write.bind(request);
const oldEnd = request.end.bind(request);
request.on('data', chunk => {
chunks.push(decodeURIComponent(chunk.toString()))
return oldWrite(chunk);
});
request.on('end', (chunk) => {
if (chunk) {
chunks.push(decodeURIComponent(chunk.toString()));
}
oldEnd(chunk);
return resolve(chunks.join(''))
});
});
}
22 changes: 15 additions & 7 deletions src/http-plugins/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { ClientRequest, IncomingHttpHeaders, IncomingMessage, ServerResponse } from "http";
import { ClientRequest, IncomingMessage, ServerResponse } from "http";

export type Plugin = {
shouldParseRequest?(request: ClientRequest | IncomingMessage): boolean;
shouldParseResponse?(response: IncomingMessage | ServerResponse<IncomingMessage>): boolean;
export class HttpPlugin {
parseIncommingMessage?(request: IncomingMessage): Record<string, unknown>;
parseClientRequest?(request: ClientRequest): Record<string, unknown>;
captureBody?: boolean;
name: string;
}
captureBody = false
name = 'base-plugin-should-extend'
constructor() {

}

shouldParseRequest(request: ClientRequest | IncomingMessage): boolean {
return false;
}
shouldParseResponse(response: IncomingMessage | ServerResponse<IncomingMessage>): boolean {
return false
}
}
24 changes: 12 additions & 12 deletions src/http-plugins/stripe.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import { ClientRequest, IncomingHttpHeaders, IncomingMessage, ServerResponse } from "http";
import { Plugin } from "./plugin.ts";
import { ClientRequest, IncomingMessage } from "http";
import { HttpPlugin } from "./plugin.ts";

export class StripePlugin extends HttpPlugin implements HttpPlugin {
captureBody = true
name = 'stripe'

export const StripePlugin: Plugin = {
captureBody: true,
name: 'stripe',
shouldParseRequest(request: ClientRequest | IncomingMessage): boolean {

if (request instanceof ClientRequest && request.host?.includes('api.stripe.com')) {
return true;
}
return false;
},
shouldParseResponse(response: IncomingMessage | ServerResponse<IncomingMessage>) {
return false;
},
}

parseClientRequest(request: ClientRequest) {
const method = request.method;

const [version, entity, entityIdOrOperation, operation] = request.path.split('/');

return {
Expand All @@ -29,4 +27,6 @@ export const StripePlugin: Plugin = {
}
}
}
}
}

const plugin = new StripePlugin();
13 changes: 6 additions & 7 deletions src/http-plugins/vercel.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { ClientRequest, IncomingHttpHeaders, IncomingMessage, ServerResponse } from "http";
import { Plugin } from "./plugin.ts";
import { ClientRequest, IncomingMessage } from "http";
import { HttpPlugin } from "./plugin.ts";

export const VercelPlugin: Plugin = {
name: 'vercel',
export class VercelPlugin extends HttpPlugin implements HttpPlugin {
name = 'vercel';
shouldParseRequest(request: ClientRequest | IncomingMessage): boolean {

if (request instanceof IncomingMessage && request.headers['x-vercel-id']) {
return true;
}
return false;
},

}
parseIncommingMessage(request: IncomingMessage) {

const headers = request.headers;
Expand Down
62 changes: 36 additions & 26 deletions src/http.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,31 @@
import { Span } from "@opentelemetry/api";
import { ClientRequest, IncomingHttpHeaders, IncomingMessage } from "http";
import { Plugin } from "./http-plugins/plugin.ts";
import { ClientRequest, IncomingMessage } from "http";
import { HttpPlugin } from "./http-plugins/plugin.ts";
import { flatten } from "flat";
import { HttpInstrumentation, HttpInstrumentationConfig } from "@opentelemetry/instrumentation-http";
import { captureBody } from "./http-plugins/captureBody.ts";
import { StripePlugin } from "./index.ts";


type BetterHttpInstrumentationOptions = {
plugins: Plugin[],
plugins?: HttpPlugin[],
requestHook?: HttpInstrumentationConfig['requestHook']
responseHook?: HttpInstrumentationConfig['responseHook']
ignoreIncomingRequestHook?: HttpInstrumentationConfig['ignoreIncomingRequestHook']
ignoreOutgoingRequestHook?: HttpInstrumentationConfig['ignoreOutgoingRequestHook']
startIncomingSpanHook?: HttpInstrumentationConfig['startIncomingSpanHook']
startOutgoingSpanHook?: HttpInstrumentationConfig['startOutgoingSpanHook']
}

function captureBody(request: ClientRequest): Promise<string> {
return new Promise((resolve, reject) => {
const chunks: string[] = [];
const oldWrite = request.write.bind(request);
const oldEnd = request.end.bind(request);
request.on('data', chunk => {
chunks.push(decodeURIComponent(chunk.toString()))
return oldWrite(chunk);
});
request.on('end', (chunk) => {
if (chunk) {
chunks.push(decodeURIComponent(chunk.toString()));
}
oldEnd(chunk);
return resolve(chunks.join(''))
});
});
}

export function betterHttpInstrumentation (options: BetterHttpInstrumentationOptions) {
export function _betterHttpInstrumentation (options: BetterHttpInstrumentationOptions = {}) {
options.plugins = options.plugins || [];
return {
requestHook (span: Span, request: ClientRequest | IncomingMessage) {
if (request instanceof ClientRequest) {
const plugin = options.plugins.find(plugin => plugin.shouldParseRequest && plugin.shouldParseRequest(request));


span.setAttribute('http.plugin.name', plugin.name);

if (plugin.captureBody) {
captureBody(request).then(body => {
span.setAttributes(flatten({ body }));
Expand All @@ -49,7 +42,24 @@ export function betterHttpInstrumentation (options: BetterHttpInstrumentationOpt
span.setAttributes(flatten(attributes));
}
}
}

if(options.requestHook) {
options.requestHook(span, request);
}
},

}
}


export class BetterHttpInstrumentation extends HttpInstrumentation {
constructor(options: BetterHttpInstrumentationOptions = {}) {
super({
..._betterHttpInstrumentation(options),
responseHook: options.responseHook,
ignoreIncomingRequestHook: options.ignoreIncomingRequestHook,
ignoreOutgoingRequestHook: options.ignoreOutgoingRequestHook,
startIncomingSpanHook: options.startIncomingSpanHook,
startOutgoingSpanHook: options.startOutgoingSpanHook,
})
}
}
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export { BaselimeSDK } from './baselime.ts';
export { tracing as trpcTracingMiddleware } from './trpc.ts';
export { betterHttpInstrumentation } from './http.ts';
export { BetterHttpInstrumentation } from './http.ts';
export { StripePlugin } from './http-plugins/stripe.ts';
export { Plugin } from './http-plugins/plugin.ts';
export { HttpPlugin } from './http-plugins/plugin.ts';
export { VercelPlugin } from './http-plugins/vercel.ts';
3 changes: 2 additions & 1 deletion tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export default defineConfig({
clean: true,
format: ['esm', 'cjs'],
target: 'node18',
noExternal: [/^.*/],
minify: false,
// for now we include flat in the bundle because it is not exported correctly for both esm and cjs
noExternal: [/flat/],
})

0 comments on commit 1fad416

Please sign in to comment.