rsocket-rpc-tracing
Version:
RSocket JavaScript RPC Tracing Support
186 lines (158 loc) • 4.58 kB
Flow
import {UTF8Encoder, BufferEncoder, createBuffer} from 'rsocket-core';
import {ISubscriber} from 'rsocket-types';
import {Flowable, Single} from 'rsocket-flowable';
import {SpanSubscriber} from './SpanSubscriber';
import {createSpanSingle} from './SpanSingle';
import {SpanContext, Tracer, FORMAT_TEXT_MAP} from 'opentracing';
import {getTracing} from 'rsocket-rpc-frames';
export function deserializeTraceData(tracer, metadata) {
if (!tracer) {
return null;
}
const tracingData = getTracing(metadata);
if (BufferEncoder.byteLength(tracingData) <= 0) {
return null;
}
return tracer.extract(FORMAT_TEXT_MAP, bufferToMap(tracingData));
}
export function mapToBuffer(map: Object): Buffer {
if (!map || Object.keys(map).length <= 0) {
return createBuffer(0);
}
const aggregatedTags = Object.keys(map).reduce(
(aggregate, key) => {
const val = map[key];
const keyLen = UTF8Encoder.byteLength(key);
const keyBuf = createBuffer(keyLen);
UTF8Encoder.encode(key, keyBuf, 0, keyLen);
const valLen = UTF8Encoder.byteLength(val);
const valBuf = createBuffer(valLen);
UTF8Encoder.encode(val, valBuf, 0, valLen);
const newEntries = aggregate.entries;
newEntries.push({keyLen, keyBuf, valLen, valBuf});
return {
//4 for the sizes plus the actual key and actual value
totalSize: aggregate.totalSize + 4 + keyLen + valLen,
entries: newEntries,
};
},
{totalSize: 0, entries: []},
);
let offset = 0;
const resultBuf = createBuffer(aggregatedTags.totalSize);
aggregatedTags.entries.forEach(entry => {
resultBuf.writeUInt16BE(entry.keyLen, offset);
offset += 2; //2 bytes for key length
BufferEncoder.encode(
entry.keyBuf,
resultBuf,
offset,
offset + entry.keyLen,
);
offset += entry.keyLen;
resultBuf.writeUInt16BE(entry.valLen, offset);
offset += 2;
BufferEncoder.encode(
entry.valBuf,
resultBuf,
offset,
offset + entry.valLen,
);
offset += entry.valLen;
});
return resultBuf;
}
export function bufferToMap(buffer: Buffer): Object {
const result = {};
let offset = 0;
while (offset < buffer.length) {
let keyLen = buffer.readUInt16BE(offset);
offset += 2;
let key = UTF8Encoder.decode(buffer, offset, offset + keyLen);
offset += keyLen;
let valLen = buffer.readUInt16BE(offset);
offset += 2;
let value = UTF8Encoder.decode(buffer, offset, offset + valLen);
offset += valLen;
result[key] = value;
}
return result;
}
export function trace<T>(
tracer?: Tracer,
name?: String,
...tags: Object
): Object => (Flowable<T>) => Flowable<T> {
if (tracer && name) {
return (metadata: Object) => {
return (flowable: Flowable<T>) => {
return flowable.lift((subscriber: ISubscriber<T>) => {
return new SpanSubscriber(
subscriber,
tracer,
name,
null,
metadata,
...tags,
);
});
};
};
} else {
return (map: Object) => (publisher: Flowable<T>) => publisher;
}
}
export function traceAsChild<T>(
tracer?: Tracer,
name?: String,
...tags: Object
): SpanContext => (Flowable<T>) => Flowable<T> {
if (tracer && name) {
return (context: SpanContext) => {
return (flowable: Flowable<T>) => {
return flowable.lift((subscriber: ISubscriber<T>) => {
return new SpanSubscriber(
subscriber,
tracer,
name,
context,
null,
...tags,
);
});
};
};
} else {
return (context: SpanContext) => (publisher: Flowable<T>) => publisher;
}
}
export function traceSingle<T>(
tracer?: Tracer,
name?: String,
...tags: Object
): Object => (Single<T>) => Single<T> {
if (tracer && name) {
return (metadata: Object) => {
return (single: Single<T>) => {
return createSpanSingle(single, tracer, name, null, metadata, ...tags);
};
};
} else {
return (map: Object) => (single: Single<T>) => single;
}
}
export function traceSingleAsChild<T>(
tracer?: Tracer,
name?: String,
...tags: Object
): SpanContext => (Single<T>) => Single<T> {
if (tracer && name) {
return (context: SpanContext) => {
return (single: Single<T>) => {
return createSpanSingle(single, tracer, name, context, null, ...tags);
};
};
} else {
return (context: SpanContext) => (single: Single<T>) => single;
}
}