Otel collector
git clone https://github.com/open-telemetry/opentelemetry-collector-contrib.git
cd opentelemetry-collector-contrib/examples/demo
docker-compose.yml
version: "2"
services:
# Jaeger
jaeger-all-in-one:
image: jaegertracing/all-in-one:latest
ports:
- "16686:16686"
#- "14268:14268"
- "14250"
# Collector
otel-collector:
image: ${OTELCOL_IMG}
command: ["--config=/etc/otel-collector-config.yaml", "${OTELCOL_ARGS}"]
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- "1888:1888" # pprof extension
- "8888:8888" # Prometheus metrics exposed by the collector
- "8889:8889" # Prometheus exporter metrics
- "13133:13133" # health_check extension
- "4317:4317" # OTLP gRPC receiver
- "4318:4318" # OTLP HTTP receiver
- "14268:14268" # Jaeger HTTP receiver
- "55679:55679" # zpages extension
depends_on:
- jaeger-all-in-one
otel-collector-config.yaml
receivers:
jaeger:
protocols:
thrift_http: # 14268
otlp:
protocols:
grpc:
http:
cors:
allowed_origins:
- '*'
exporters:
#prometheus:
# endpoint: "0.0.0.0:8889"
# const_labels:
# label1: value1
logging:
logLevel: debug
#zipkin:
# endpoint: "http://zipkin-all-in-one:9411/api/v2/spans"
# format: proto
jaeger:
endpoint: jaeger-all-in-one:14250
tls:
insecure: true
processors:
batch:
extensions:
health_check:
pprof:
endpoint: :1888
zpages:
endpoint: :55679
service:
extensions: [pprof, zpages, health_check]
pipelines:
traces:
receivers: [jaeger,otlp]
processors: [batch]
#exporters: [logging, zipkin, jaeger]
exporters: [jaeger, logging]
#exporters: [logging]
#metrics:
# receivers: [otlp]
# processors: [batch]
# exporters: [logging, prometheus]
agenda.json
{
"resourceSpans": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": {
"stringValue": "Angular Sample App"
}
},
{
"key": "telemetry.sdk.language",
"value": {
"stringValue": "webjs"
}
},
{
"key": "telemetry.sdk.name",
"value": {
"stringValue": "opentelemetry"
}
},
{
"key": "telemetry.sdk.version",
"value": {
"stringValue": "1.5.0"
}
}
],
"droppedAttributesCount": 0
},
"scopeSpans": [
{
"scope": {
"name": "@jufab/opentelemetry-angular-interceptor",
"version": "1.1.0-2"
},
"spans": [
{
"traceId": "6c697d06c5eecac7fd62fbe4d02521e2",
"spanId": "1c2be1cbaf16deec",
"name": "HTTP GET",
"kind": 3,
"startTimeUnixNano": 1660539648581100000,
"endTimeUnixNano": 1660539648595000000,
"attributes": [
{
"key": "http.method",
"value": {
"stringValue": "GET"
}
},
{
"key": "http.url",
"value": {
"stringValue": "http://django-max:8081/"
}
},
{
"key": "http.host",
"value": {
"stringValue": "django-max:8081"
}
},
{
"key": "http.scheme",
"value": {
"stringValue": "http"
}
},
{
"key": "http.target",
"value": {
"stringValue": "/"
}
},
{
"key": "http.user_agent",
"value": {
"stringValue": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"
}
},
{
"key": "http.status_code",
"value": {
"intValue": 200
}
}
],
"droppedAttributesCount": 0,
"events": [],
"droppedEventsCount": 0,
"status": {
"code": 0
},
"links": [],
"droppedLinksCount": 0
}
]
}
]
}
]
}
k6 GRPC
import grpc from 'k6/net/grpc';
import { check, sleep } from 'k6';
const client = new grpc.Client();
client.load(['definitions'], 'opentelemetry/proto/collector/trace/v1/trace_service.proto');
//client.load(['definitions'], 'hello.proto');
//client.load(['definitions'], 'otel.proto');
//client.load('otel.proto');
//const data0 = JSON.parse(open("./traces.txt"));
//const data0 = JSON.parse(open("./trace0.json"));
const data0 = JSON.parse(open("./agenda.json"));
const d = new Date().getTime()
export default () => {
client.connect('lab003:4317', {
// plaintext: false
plaintext: true,
});
const data = { greeting: 'Bert' };
data0.resourceSpans[0].scopeSpans[0].spans[0].startTimeUnixNano = d*1000000
data0.resourceSpans[0].scopeSpans[0].spans[0].endTimeUnixNano = d*1000000
//const traceparent = "00-9f85218a78664e1d3cba6b438e90bd67-eee5976fa7571dd3-01"
const traceparent = "00-9f85218a78664e1d3cba6b438e90bd67-eee5976fa7500000-01"
//data0.resourceSpans[0].scopeSpans[0].spans[0].traceId = traceparent
let traceid = "c4d3ac8dffee87aec342d788d84679a1"
let spanid = "9a98f4b1a7417410"
let b64traceid = "YzRkM2FjOGRmZmVlODdhZWMzNDJkNzg4ZDg0Njc5YTEK"
let b64spanid = "OWE5OGY0YjFhNzQxNzQxMAo="
traceid = "kDMI7LTxLxTj220awNARJw=="
spanid = "9ir6veJ4Hdw="
data0.resourceSpans[0].scopeSpans[0].spans[0].traceId = traceid
data0.resourceSpans[0].scopeSpans[0].spans[0].spanId = spanid
//data0.resourceSpans[0].scopeSpans[0].spans[0].traceId = "6c697d06c5eecac7fd62fbe4d02521e2"
//data0.resourceSpans[0].scopeSpans[0].spans[0].spanId = "1c2be1cbaf16deec"
// "TraceID": "c4d3ac8dffee87aec342d788d84679a1",
// "SpanID": "9a98f4b1a7417410",
//
//"6c697d06c5eecac7fd62fbe4d02521e2",
//"spanId": "1c2be1cbaf16deec",
console.log(JSON.stringify(data0, null, 4))
const response = client.invoke('/opentelemetry.proto.collector.trace.v1.TraceService/Export', data0);
//const response = client.invoke('otel.TraceService/Export', data0);
check(response, {
'status is OK': (r) => {
console.log('r', r)
return r && r.status === grpc.StatusOK
},
});
console.log(JSON.stringify(response.message));
client.close();
sleep(1);
};
ramp up iterations
export const options = {
scenarios: {
ramping_vus_scenario: {
executor: "ramping-vus",
startTime: '0s',
stages: [{
target: 100,
duration: "15s"
}
]
},
}
}
get proto
mkdir definitions
git clone https://github.com/open-telemetry/opentelemetry-proto.git
cp -r opentelemetry-proto/opentelemetry definitions/
K6 HTTP
import { sleep, check } from "k6";
import http from "k6/http";
import { htmlReport } from "https://raw.githubusercontent.com/benc-uk/k6-reporter/main/dist/bundle.js";
//export const options = {
// scenarios: {
// ramping_vus_scenario: {
// executor: "ramping-vus",
// startTime: '0s',
// stages: [{
// target: 10,
// duration: "1s"
// //target: 100,
// //duration: "15s"
// }
// ]
// },
// }
//}
const d = new Date().getTime()
const payload = JSON.parse(open("./agenda.json"));
export default function () {
payload.resourceSpans[0].scopeSpans[0].spans[0].startTimeUnixNano = d*1000000
payload.resourceSpans[0].scopeSpans[0].spans[0].endTimeUnixNano = d*1000000
let res = http.post("http://lab003:4318/v1/traces", JSON.stringify(payload), {
headers: {
"Content-Type": "application/json"
}});
console.log(res);
}
export function handleSummary(data) {
return {
"summary.html": htmlReport(data),
};
}
Caveats
Trace/Span ids base64 encoded when used in proto-JSON representation #786
https://github.com/open-telemetry/opentelemetry-specification/issues/786
https://cryptii.com/pipes/base64-to-binary
kDMI7LTxLxTj220awNARJw==
import base64
import random
def generate_span_id():
return format(random.getrandbits(64), "016x")
def generate_trace_id():
return format(random.getrandbits(128), "032x")
# from
# 903308ecb4f12f14e3db6d1ac0d01127
# to
# kDMI7LTxLxTj220awNARJw==
if 1:
a = "903308ecb4f12f14e3db6d1ac0d01127"
bfh = bytes.fromhex(a)
print(bfh)
print("bytes.fromhex", bfh)
print("baseencode", base64.b64encode(bfh)) # b'kDMI7LTxLxTj220awNARJw=='
# from
# kDMI7LTxLxTj220awNARJw==
# to
# 903308ecb4f12f14e3db6d1ac0d01127
if 1:
a = "kDMI7LTxLxTj220awNARJw=="
benc = a.encode()
print("benc", benc) # b'kDMI7LTxLxTj220awNARJw=='
bdec = base64.b64decode(benc)
print("bdec", bdec)
print("tohex", bdec.hex()) # 903308ecb4f12f14e3db6d1ac0d01127
K6 gen traceid spanid
https://blog.abelotech.com/posts/generate-random-values-nodejs-javascript/
traceid = base64ArrayBuffer(crypto.randomBytes(16))
spanid = base64ArrayBuffer(crypto.randomBytes(8))
k6
https://community.k6.io/t/base64-encode-arraybuffer/1152/2
var str = "testing"; // just for illustrative purposes, base64ArrayBuffer() seems to work for non-ASCII values as well
var buffer = new ArrayBuffer(str.length);
var typedArr = new Uint8Array(buffer);
for (var i = 0; i < str.length; i++) {
typedArr[i] = str.charCodeAt(i);
}
console.log(typedArr);
console.log(base64ArrayBuffer(buffer));
https://gist.github.com/jonleighton/958841#gistcomment-1953137
function base64ArrayBuffer(arrayBuffer) {
var base64 = ''
var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
var bytes = new Uint8Array(arrayBuffer)
var byteLength = bytes.byteLength
var byteRemainder = byteLength % 3
var mainLength = byteLength - byteRemainder
var a, b, c, d
var chunk
// Main loop deals with bytes in chunks of 3
for (var i = 0; i < mainLength; i = i + 3) {
// Combine the three bytes into a single integer
chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]
// Use bitmasks to extract 6-bit segments from the triplet
a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18
b = (chunk & 258048) >> 12 // 258048 = (2^6 - 1) << 12
c = (chunk & 4032) >> 6 // 4032 = (2^6 - 1) << 6
d = chunk & 63 // 63 = 2^6 - 1
// Convert the raw binary segments to the appropriate ASCII encoding
base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]
}
// Deal with the remaining bytes and padding
if (byteRemainder == 1) {
chunk = bytes[mainLength]
a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2
// Set the 4 least significant bits to zero
b = (chunk & 3) << 4 // 3 = 2^2 - 1
base64 += encodings[a] + encodings[b] + '=='
} else if (byteRemainder == 2) {
chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]
a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10
b = (chunk & 1008) >> 4 // 1008 = (2^6 - 1) << 4
// Set the 2 least significant bits to zero
c = (chunk & 15) << 2 // 15 = 2^4 - 1
base64 += encodings[a] + encodings[b] + encodings[c] + '='
}
return base64
}