From b938e9d76866f2a66d0052675b4b9cf2e41f7641 Mon Sep 17 00:00:00 2001 From: tyk Date: Tue, 18 May 2021 18:46:27 +0800 Subject: [PATCH 01/13] ioredis plugin --- src/plugins/IORedis.ts | 43 ++++++++++++++++++++++++++++++++++++++++++ src/trace/Component.ts | 3 ++- 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/plugins/IORedis.ts diff --git a/src/plugins/IORedis.ts b/src/plugins/IORedis.ts new file mode 100644 index 0000000..5e21086 --- /dev/null +++ b/src/plugins/IORedis.ts @@ -0,0 +1,43 @@ +import PluginInstaller from 'core/PluginInstaller'; +import SwPlugin, { wrapPromise } from '../core/SwPlugin'; +import { SpanLayer } from '../proto/language-agent/Tracing_pb'; +import Tag from '../Tag'; +import { Component } from '../trace/Component'; +import ContextManager from '../trace/context/ContextManager'; + +class IORedisPlugin implements SwPlugin { + readonly module = 'ioredis'; + readonly versions = '*'; + + install(installer: PluginInstaller): void { + const Redis = installer.require('ioredis'); + + this.interceptOperation(Redis, 'sendCommand'); + } + + interceptOperation(Cls: any, operation: string): void { + const _original = Cls.prototype[operation]; + + if (!_original) + return; + + Cls.prototype[operation] = function (...args: any[]) { + const command = args[0]; + const host = `${this.options.host}:${this.options.port}`; + const span = ContextManager.current.newExitSpan(`redis/${command.name}`, host, Component.REDIS); + + span.start(); + span.component = Component.REDIS; + span.layer = SpanLayer.CACHE; + span.peer = host; + span.tag(Tag.cacheType('Redis')); + span.tag(Tag.cacheInstance(`${this.condition.select}`)); + + const ret = wrapPromise(span, _original.apply(this, args)); + span.async(); + return ret; + } + } +} + +export default new IORedisPlugin(); \ No newline at end of file diff --git a/src/trace/Component.ts b/src/trace/Component.ts index 64d47bf..1680ec3 100644 --- a/src/trace/Component.ts +++ b/src/trace/Component.ts @@ -21,6 +21,7 @@ export class Component { static readonly UNKNOWN = new Component(0); static readonly HTTP = new Component(2); static readonly MYSQL = new Component(5); + static readonly REDIS = new Component(7); static readonly MONGODB = new Component(9); static readonly POSTGRESQL = new Component(22); static readonly HTTP_SERVER = new Component(49); @@ -30,5 +31,5 @@ export class Component { static readonly AXIOS = new Component(4005); static readonly MONGOOSE = new Component(4006); - constructor(public readonly id: number) {} + constructor(public readonly id: number) { } } From 240ef2026c396a400188b59ed76e3b59c1856f0a Mon Sep 17 00:00:00 2001 From: tyk Date: Tue, 18 May 2021 18:52:09 +0800 Subject: [PATCH 02/13] add cache tag --- src/Tag.ts | 16 ++++++++++++++++ src/plugins/IORedis.ts | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Tag.ts b/src/Tag.ts index 44e30ff..150f1b6 100644 --- a/src/Tag.ts +++ b/src/Tag.ts @@ -36,6 +36,8 @@ export default { mqBrokerKey: 'mq.broker', mqTopicKey: 'mq.topic', mqQueueKey: 'mq.queue', + cacheTypeKey: 'cache.type', + cacheInstanceKey: 'cache.instance', httpStatusCode(val: string | number | undefined): Tag { return { @@ -121,4 +123,18 @@ export default { val: `${val}`, } as Tag; }, + cacheType(val: string | undefined) { + return { + key: this.cacheTypeKey, + overridable: true, + val: `${val}` + } as Tag; + }, + cacheInstance(val: string | undefined) { + return { + key: this.cacheInstanceKey, + overridable: true, + val: `${val}` + } as Tag; + }, }; diff --git a/src/plugins/IORedis.ts b/src/plugins/IORedis.ts index 5e21086..d0c329c 100644 --- a/src/plugins/IORedis.ts +++ b/src/plugins/IORedis.ts @@ -11,7 +11,7 @@ class IORedisPlugin implements SwPlugin { install(installer: PluginInstaller): void { const Redis = installer.require('ioredis'); - + this.interceptOperation(Redis, 'sendCommand'); } @@ -24,7 +24,7 @@ class IORedisPlugin implements SwPlugin { Cls.prototype[operation] = function (...args: any[]) { const command = args[0]; const host = `${this.options.host}:${this.options.port}`; - const span = ContextManager.current.newExitSpan(`redis/${command.name}`, host, Component.REDIS); + const span = ContextManager.current.newExitSpan(`redis/${command?.name}`, host, Component.REDIS); span.start(); span.component = Component.REDIS; From 928f100a1847f0979d00f58afaadcaf2b0a73413 Mon Sep 17 00:00:00 2001 From: tyk Date: Wed, 19 May 2021 11:03:52 +0800 Subject: [PATCH 03/13] add ioredis test --- package-lock.json | 95 +++++++++++++++++ package.json | 4 +- tests/plugins/ioredis/client.ts | 38 +++++++ tests/plugins/ioredis/docker-compose.yml | 84 +++++++++++++++ tests/plugins/ioredis/expected.data.yaml | 127 +++++++++++++++++++++++ tests/plugins/ioredis/server.ts | 49 +++++++++ tests/plugins/ioredis/test.ts | 57 ++++++++++ 7 files changed, 453 insertions(+), 1 deletion(-) create mode 100644 tests/plugins/ioredis/client.ts create mode 100644 tests/plugins/ioredis/docker-compose.yml create mode 100644 tests/plugins/ioredis/expected.data.yaml create mode 100644 tests/plugins/ioredis/server.ts create mode 100644 tests/plugins/ioredis/test.ts diff --git a/package-lock.json b/package-lock.json index 62a9e49..7478ff6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -953,6 +953,15 @@ "@types/node": "*" } }, + "@types/ioredis": { + "version": "4.26.4", + "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.26.4.tgz", + "integrity": "sha512-QFbjNq7EnOGw6d1gZZt2h26OFXjx7z+eqEnbCHSrDI1OOLEgOHMKdtIajJbuCr9uO+X9kQQRe7Lz6uxqxl5XKg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", @@ -2138,6 +2147,12 @@ "wrap-ansi": "^2.0.0" } }, + "cluster-key-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", + "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==", + "dev": true + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -3837,6 +3852,41 @@ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" }, + "ioredis": { + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.27.2.tgz", + "integrity": "sha512-7OpYymIthonkC2Jne5uGWXswdhlua1S1rWGAERaotn0hGJWTSURvxdHA9G6wNbT/qKCloCja/FHsfKXW8lpTmg==", + "dev": true, + "requires": { + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.1", + "denque": "^1.1.0", + "lodash.defaults": "^4.2.0", + "lodash.flatten": "^4.4.0", + "p-map": "^2.1.0", + "redis-commands": "1.7.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -5219,6 +5269,18 @@ "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=" }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", + "dev": true + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "dev": true + }, "lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", @@ -6121,6 +6183,12 @@ "p-limit": "^2.2.0" } }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -6498,6 +6566,27 @@ "util-deprecate": "~1.0.1" } }, + "redis-commands": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", + "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==", + "dev": true + }, + "redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=", + "dev": true + }, + "redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "dev": true, + "requires": { + "redis-errors": "^1.0.0" + } + }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -7299,6 +7388,12 @@ } } }, + "standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "dev": true + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", diff --git a/package.json b/package.json index 7fccce8..0ada9f8 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "devDependencies": { "@types/express": "^4.17.9", "@types/google-protobuf": "^3.7.2", + "@types/ioredis": "^4.26.4", "@types/jest": "^26.0.15", "@types/node": "^14.0.11", "@types/semver": "^7.2.0", @@ -47,8 +48,9 @@ "amqplib": "^0.7.0", "axios": "^0.21.0", "express": "^4.17.1", - "grpc_tools_node_protoc_ts": "^4.0.0", "grpc-tools": "^1.10.0", + "grpc_tools_node_protoc_ts": "^4.0.0", + "ioredis": "^4.27.2", "jest": "^26.6.3", "mongodb": "^3.6.4", "mongoose": "^5.12.2", diff --git a/tests/plugins/ioredis/client.ts b/tests/plugins/ioredis/client.ts new file mode 100644 index 0000000..2f33af1 --- /dev/null +++ b/tests/plugins/ioredis/client.ts @@ -0,0 +1,38 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import * as http from 'http'; +import agent from '../../../src'; + +agent.start({ + serviceName: 'client', + maxBufferSize: 1000, +}) + +const server = http.createServer((req, res) => { + http + .request(`http://${process.env.SERVER || 'localhost:5000'}${req.url}`, (r) => { + let data = ''; + r.on('data', (chunk) => (data += chunk)); + r.on('end', () => res.end(data)); + }) + .end(); +}); + +server.listen(5001, () => console.info('Listening on port 5001...')); diff --git a/tests/plugins/ioredis/docker-compose.yml b/tests/plugins/ioredis/docker-compose.yml new file mode 100644 index 0000000..28c9fba --- /dev/null +++ b/tests/plugins/ioredis/docker-compose.yml @@ -0,0 +1,84 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +version: "2.1" + +services: + collector: + extends: + file: ../common/base-compose.yml + service: collector + networks: + - traveling-light + + redis: + container_name: redis + ports: + - 6379:6379 + healthcheck: + test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/6379"] + interval: 5s + timeout: 60s + retries: 120 + image: "redis:latest" + networks: + - traveling-light + + server: + extends: + file: ../common/base-compose.yml + service: agent + ports: + - 5000:5000 + environment: + REDIS_HOST: redis + volumes: + - .:/app/tests/plugins/ioredis + healthcheck: + test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5000"] + interval: 5s + timeout: 60s + retries: 120 + entrypoint: + ["bash", "-c", "npx ts-node /app/tests/plugins/ioredis/server.ts"] + depends_on: + collector: + condition: service_healthy + postgres: + condition: service_healthy + + client: + extends: + file: ../common/base-compose.yml + service: agent + ports: + - 5001:5001 + environment: + SERVER: server:5000 + healthcheck: + test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5001"] + interval: 5s + timeout: 60s + retries: 120 + entrypoint: + ["bash", "-c", "npx ts-node /app/tests/plugins/ioredis/client.ts"] + depends_on: + server: + condition: service_healthy + +networks: + traveling-light: diff --git a/tests/plugins/ioredis/expected.data.yaml b/tests/plugins/ioredis/expected.data.yaml new file mode 100644 index 0000000..c8de50e --- /dev/null +++ b/tests/plugins/ioredis/expected.data.yaml @@ -0,0 +1,127 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +segmentItems: + - serviceName: server + segmentSize: 1 + segments: + - segmentId: not null + spans: + - operationName: redis/get + operationId: 0 + parentSpanId: 0 + spanId: 3 + spanLayer: Cache + startTime: gt 0 + endTime: gt 0 + componentId: 22 + spanType: Exit + peer: redis:6379 + skipAnalysis: false + tags: + - { key: cache.type, value: Redis } + - { key: cache.instance, value: 0 } + - operationName: redis/set + operationId: 0 + parentSpanId: 0 + spanId: 2 + spanLayer: Cache + startTime: gt 0 + endTime: gt 0 + componentId: 22 + spanType: Exit + peer: redis:6379 + skipAnalysis: false + tags: + - { key: cache.type, value: Redis } + - { key: cache.instance, value: 0 } + - operationName: redis/auth + operationId: 0 + parentSpanId: 0 + spanId: 1 + spanLayer: Cache + startTime: gt 0 + endTime: gt 0 + componentId: 22 + spanType: Exit + peer: redis:6379 + skipAnalysis: false + tags: + - { key: cache.type, value: Redis } + - { key: cache.instance, value: 0 } + - operationName: /ioredis + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 49 + spanType: Entry + peer: not null + skipAnalysis: false + tags: + - { key: http.url, value: "http://server:5000/ioredis" } + - { key: http.method, value: GET } + - { key: http.status.code, value: "200" } + - { key: http.status.msg, value: OK } + refs: + - parentEndpoint: "" + networkAddress: server:5000 + refType: CrossProcess + parentSpanId: 1 + parentTraceSegmentId: not null + parentServiceInstance: not null + parentService: client + traceId: not null + - serviceName: client + segmentSize: 1 + segments: + - segmentId: not null + spans: + - operationName: /ioredis + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 49 + spanType: Entry + peer: not null + skipAnalysis: false + tags: + - { key: http.url, value: "http://localhost:5001/ioredis" } + - { key: http.method, value: GET } + - { key: http.status.code, value: "200" } + - { key: http.status.msg, value: OK } + - operationName: /ioredis + operationId: 0 + parentSpanId: 0 + spanId: 1 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 2 + spanType: Exit + peer: server:5000 + skipAnalysis: false + tags: + - { key: http.url, value: "http://server:5000/ioredis" } + - { key: http.method, value: GET } + - { key: http.status.code, value: "200" } + - { key: http.status.msg, value: OK } diff --git a/tests/plugins/ioredis/server.ts b/tests/plugins/ioredis/server.ts new file mode 100644 index 0000000..3ebf8e8 --- /dev/null +++ b/tests/plugins/ioredis/server.ts @@ -0,0 +1,49 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import * as http from 'http'; +import Redis from 'ioredis'; +import agent from '../../../src'; +import assert from 'assert'; + +agent.start({ + serviceName: 'server', + maxBufferSize: 1000, +}) + +const server = http.createServer((req, res) => { + (async () => { + const cacheKey = 'now'; + const now = '' + Date.now(); + + const client = new Redis({ + host: process.env.REDIS_HOST || 'redis', + }); + await client.set(cacheKey, now); + const _now = await client.get(cacheKey); + assert.strictEqual(now, _now); + + res.end(_now); + })().catch((err: Error) => { + res.statusCode = 500; + res.end(err.message); + }); +}) + +server.listen(5000, () => console.info('Listening on port 5000...')); diff --git a/tests/plugins/ioredis/test.ts b/tests/plugins/ioredis/test.ts new file mode 100644 index 0000000..8a8b423 --- /dev/null +++ b/tests/plugins/ioredis/test.ts @@ -0,0 +1,57 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import * as path from 'path'; +import { DockerComposeEnvironment, StartedDockerComposeEnvironment, Wait } from 'testcontainers'; +import axios from 'axios'; +import waitForExpect from 'wait-for-expect'; +import { promises as fs } from 'fs'; + +const rootDir = path.resolve(__dirname); + +describe('plugin tests', () => { + let compose: StartedDockerComposeEnvironment; + + beforeAll(async () => { + compose = await new DockerComposeEnvironment(rootDir, 'docker-compose.yml') + .withWaitStrategy('client', Wait.forHealthCheck()) + .withWaitStrategy('redis', Wait.forHealthCheck()) + .up(); + }); + + afterAll(async () => { + await compose.down(); + }); + + it(__filename, async () => { + await waitForExpect(async () => expect((await axios.get('http://localhost:5001/ioredis')).status).toBe(200)); + + const expectedData = await fs.readFile(path.join(rootDir, 'expected.data.yaml'), 'utf8'); + + try { + await waitForExpect(async () => + expect((await axios.post('http://localhost:12800/dataValidate', expectedData)).status).toBe(200), + ); + } catch (e) { + const actualData = (await axios.get('http://localhost:12800/receiveData')).data; + console.info({ actualData }); + throw e; + } + }); +}); From 1881d6a7a99588ac63c3749db68b82e375bd5ec9 Mon Sep 17 00:00:00 2001 From: tyk Date: Wed, 19 May 2021 14:02:36 +0800 Subject: [PATCH 04/13] add file header --- src/plugins/IORedis.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/plugins/IORedis.ts b/src/plugins/IORedis.ts index d0c329c..fb990c3 100644 --- a/src/plugins/IORedis.ts +++ b/src/plugins/IORedis.ts @@ -1,3 +1,22 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import PluginInstaller from 'core/PluginInstaller'; import SwPlugin, { wrapPromise } from '../core/SwPlugin'; import { SpanLayer } from '../proto/language-agent/Tracing_pb'; From 93f0d0b5e2b27572256995bf12bb8991d4fb9a59 Mon Sep 17 00:00:00 2001 From: tyk Date: Wed, 19 May 2021 14:47:23 +0800 Subject: [PATCH 05/13] fix newExitSpan --- src/plugins/IORedis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/IORedis.ts b/src/plugins/IORedis.ts index fb990c3..5c6c68a 100644 --- a/src/plugins/IORedis.ts +++ b/src/plugins/IORedis.ts @@ -43,7 +43,7 @@ class IORedisPlugin implements SwPlugin { Cls.prototype[operation] = function (...args: any[]) { const command = args[0]; const host = `${this.options.host}:${this.options.port}`; - const span = ContextManager.current.newExitSpan(`redis/${command?.name}`, host, Component.REDIS); + const span = ContextManager.current.newExitSpan(`redis/${command?.name}`, Component.REDIS); span.start(); span.component = Component.REDIS; From a7d92bbcc4171b896f921c465347af8e057cf034 Mon Sep 17 00:00:00 2001 From: tyk Date: Wed, 19 May 2021 17:18:09 +0800 Subject: [PATCH 06/13] fix ioredis test --- tests/plugins/ioredis/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/plugins/ioredis/docker-compose.yml b/tests/plugins/ioredis/docker-compose.yml index 28c9fba..f66fb04 100644 --- a/tests/plugins/ioredis/docker-compose.yml +++ b/tests/plugins/ioredis/docker-compose.yml @@ -58,7 +58,7 @@ services: depends_on: collector: condition: service_healthy - postgres: + redis: condition: service_healthy client: From d45eb6c3a433913dc92d004ea8e0da05c886ca51 Mon Sep 17 00:00:00 2001 From: tyk Date: Wed, 19 May 2021 18:02:24 +0800 Subject: [PATCH 07/13] fix ioredis unit test --- tests/plugins/ioredis/expected.data.yaml | 16 +--------------- tests/plugins/ioredis/server.ts | 9 +++++---- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/tests/plugins/ioredis/expected.data.yaml b/tests/plugins/ioredis/expected.data.yaml index c8de50e..d954091 100644 --- a/tests/plugins/ioredis/expected.data.yaml +++ b/tests/plugins/ioredis/expected.data.yaml @@ -22,20 +22,6 @@ segmentItems: - segmentId: not null spans: - operationName: redis/get - operationId: 0 - parentSpanId: 0 - spanId: 3 - spanLayer: Cache - startTime: gt 0 - endTime: gt 0 - componentId: 22 - spanType: Exit - peer: redis:6379 - skipAnalysis: false - tags: - - { key: cache.type, value: Redis } - - { key: cache.instance, value: 0 } - - operationName: redis/set operationId: 0 parentSpanId: 0 spanId: 2 @@ -49,7 +35,7 @@ segmentItems: tags: - { key: cache.type, value: Redis } - { key: cache.instance, value: 0 } - - operationName: redis/auth + - operationName: redis/set operationId: 0 parentSpanId: 0 spanId: 1 diff --git a/tests/plugins/ioredis/server.ts b/tests/plugins/ioredis/server.ts index 3ebf8e8..cf56507 100644 --- a/tests/plugins/ioredis/server.ts +++ b/tests/plugins/ioredis/server.ts @@ -25,16 +25,17 @@ import assert from 'assert'; agent.start({ serviceName: 'server', maxBufferSize: 1000, -}) +}); + +const client = new Redis({ + host: process.env.REDIS_HOST || 'redis', +}); const server = http.createServer((req, res) => { (async () => { const cacheKey = 'now'; const now = '' + Date.now(); - const client = new Redis({ - host: process.env.REDIS_HOST || 'redis', - }); await client.set(cacheKey, now); const _now = await client.get(cacheKey); assert.strictEqual(now, _now); From e28b5d2382db139ba50ac3818c2104e2cbd3aae2 Mon Sep 17 00:00:00 2001 From: tyk Date: Wed, 19 May 2021 19:28:24 +0800 Subject: [PATCH 08/13] fix import --- src/plugins/IORedisPlugin.ts | 62 ++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/plugins/IORedisPlugin.ts diff --git a/src/plugins/IORedisPlugin.ts b/src/plugins/IORedisPlugin.ts new file mode 100644 index 0000000..dfd2226 --- /dev/null +++ b/src/plugins/IORedisPlugin.ts @@ -0,0 +1,62 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import PluginInstaller from '../core/PluginInstaller'; +import SwPlugin, { wrapPromise } from '../core/SwPlugin'; +import { SpanLayer } from '../proto/language-agent/Tracing_pb'; +import Tag from '../Tag'; +import { Component } from '../trace/Component'; +import ContextManager from '../trace/context/ContextManager'; + +class IORedisPlugin implements SwPlugin { + readonly module = 'ioredis'; + readonly versions = '*'; + + install(installer: PluginInstaller): void { + const Redis = installer.require('ioredis'); + + this.interceptOperation(Redis, 'sendCommand'); + } + + interceptOperation(Cls: any, operation: string): void { + const _original = Cls.prototype[operation]; + + if (!_original) + return; + + Cls.prototype[operation] = function (...args: any[]) { + const command = args[0]; + const host = `${this.options.host}:${this.options.port}`; + const span = ContextManager.current.newExitSpan(`redis/${command?.name}`, Component.REDIS); + + span.start(); + span.component = Component.REDIS; + span.layer = SpanLayer.CACHE; + span.peer = host; + span.tag(Tag.cacheType('Redis')); + span.tag(Tag.cacheInstance(`${this.condition.select}`)); + + const ret = wrapPromise(span, _original.apply(this, args)); + span.async(); + return ret; + } + } +} + +export default new IORedisPlugin(); \ No newline at end of file From a627bcff4dc5808cc4d0505184ecd8d53929e3e1 Mon Sep 17 00:00:00 2001 From: tyk Date: Wed, 19 May 2021 19:30:40 +0800 Subject: [PATCH 09/13] rename IORedis => IORedisPlugin --- src/plugins/IORedis.ts | 62 ------------------------------------------ 1 file changed, 62 deletions(-) delete mode 100644 src/plugins/IORedis.ts diff --git a/src/plugins/IORedis.ts b/src/plugins/IORedis.ts deleted file mode 100644 index 5c6c68a..0000000 --- a/src/plugins/IORedis.ts +++ /dev/null @@ -1,62 +0,0 @@ -/*! - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import PluginInstaller from 'core/PluginInstaller'; -import SwPlugin, { wrapPromise } from '../core/SwPlugin'; -import { SpanLayer } from '../proto/language-agent/Tracing_pb'; -import Tag from '../Tag'; -import { Component } from '../trace/Component'; -import ContextManager from '../trace/context/ContextManager'; - -class IORedisPlugin implements SwPlugin { - readonly module = 'ioredis'; - readonly versions = '*'; - - install(installer: PluginInstaller): void { - const Redis = installer.require('ioredis'); - - this.interceptOperation(Redis, 'sendCommand'); - } - - interceptOperation(Cls: any, operation: string): void { - const _original = Cls.prototype[operation]; - - if (!_original) - return; - - Cls.prototype[operation] = function (...args: any[]) { - const command = args[0]; - const host = `${this.options.host}:${this.options.port}`; - const span = ContextManager.current.newExitSpan(`redis/${command?.name}`, Component.REDIS); - - span.start(); - span.component = Component.REDIS; - span.layer = SpanLayer.CACHE; - span.peer = host; - span.tag(Tag.cacheType('Redis')); - span.tag(Tag.cacheInstance(`${this.condition.select}`)); - - const ret = wrapPromise(span, _original.apply(this, args)); - span.async(); - return ret; - } - } -} - -export default new IORedisPlugin(); \ No newline at end of file From c49252b1fb3665779633255844fe0fe097d22a23 Mon Sep 17 00:00:00 2001 From: tyk Date: Fri, 21 May 2021 00:17:59 +0800 Subject: [PATCH 10/13] add ioredis connect segments --- tests/plugins/ioredis/expected.data.yaml | 32 ++++++++++++++++++------ 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/tests/plugins/ioredis/expected.data.yaml b/tests/plugins/ioredis/expected.data.yaml index d954091..0703a1b 100644 --- a/tests/plugins/ioredis/expected.data.yaml +++ b/tests/plugins/ioredis/expected.data.yaml @@ -17,24 +17,26 @@ segmentItems: - serviceName: server - segmentSize: 1 + segmentSize: 2 segments: - segmentId: not null spans: - - operationName: redis/get + - operationName: redis/info operationId: 0 - parentSpanId: 0 - spanId: 2 + parentSpanId: -1 + spanId: 0 spanLayer: Cache startTime: gt 0 endTime: gt 0 - componentId: 22 + componentId: 7 spanType: Exit peer: redis:6379 skipAnalysis: false tags: - { key: cache.type, value: Redis } - - { key: cache.instance, value: 0 } + - { key: cache.instance, value: "0" } + - segmentId: not null + spans: - operationName: redis/set operationId: 0 parentSpanId: 0 @@ -42,13 +44,27 @@ segmentItems: spanLayer: Cache startTime: gt 0 endTime: gt 0 - componentId: 22 + componentId: 7 + spanType: Exit + peer: redis:6379 + skipAnalysis: false + tags: + - { key: cache.type, value: Redis } + - { key: cache.instance, value: "0" } + - operationName: redis/get + operationId: 0 + parentSpanId: 0 + spanId: 2 + spanLayer: Cache + startTime: gt 0 + endTime: gt 0 + componentId: 7 spanType: Exit peer: redis:6379 skipAnalysis: false tags: - { key: cache.type, value: Redis } - - { key: cache.instance, value: 0 } + - { key: cache.instance, value: "0" } - operationName: /ioredis operationId: 0 parentSpanId: -1 From 658f52e5eb9fb18de4912016f92c29498e73e72b Mon Sep 17 00:00:00 2001 From: tyk Date: Sun, 23 May 2021 15:50:47 +0800 Subject: [PATCH 11/13] fix ioredis expected.data --- tests/plugins/ioredis/expected.data.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/plugins/ioredis/expected.data.yaml b/tests/plugins/ioredis/expected.data.yaml index 0703a1b..151c2b1 100644 --- a/tests/plugins/ioredis/expected.data.yaml +++ b/tests/plugins/ioredis/expected.data.yaml @@ -33,6 +33,7 @@ segmentItems: peer: redis:6379 skipAnalysis: false tags: + - { key: coldStart, value: "true" } - { key: cache.type, value: Redis } - { key: cache.instance, value: "0" } - segmentId: not null @@ -107,6 +108,7 @@ segmentItems: peer: not null skipAnalysis: false tags: + - { key: coldStart, value: "true" } - { key: http.url, value: "http://localhost:5001/ioredis" } - { key: http.method, value: GET } - { key: http.status.code, value: "200" } From 5454b6ec3833170b1138b0556959f7450510a70a Mon Sep 17 00:00:00 2001 From: tyk Date: Sun, 23 May 2021 18:16:00 +0800 Subject: [PATCH 12/13] change ioredis tag --- src/Tag.ts | 16 ---------------- src/plugins/IORedisPlugin.ts | 4 ++-- tests/plugins/ioredis/expected.data.yaml | 12 ++++++------ 3 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/Tag.ts b/src/Tag.ts index 7b129ea..8009ce6 100644 --- a/src/Tag.ts +++ b/src/Tag.ts @@ -37,8 +37,6 @@ export default { mqBrokerKey: 'mq.broker', mqTopicKey: 'mq.topic', mqQueueKey: 'mq.queue', - cacheTypeKey: 'cache.type', - cacheInstanceKey: 'cache.instance', coldStart(val: boolean = true): Tag { return { @@ -131,18 +129,4 @@ export default { val: `${val}`, } as Tag; }, - cacheType(val: string | undefined) { - return { - key: this.cacheTypeKey, - overridable: true, - val: `${val}` - } as Tag; - }, - cacheInstance(val: string | undefined) { - return { - key: this.cacheInstanceKey, - overridable: true, - val: `${val}` - } as Tag; - }, }; diff --git a/src/plugins/IORedisPlugin.ts b/src/plugins/IORedisPlugin.ts index dfd2226..adbd930 100644 --- a/src/plugins/IORedisPlugin.ts +++ b/src/plugins/IORedisPlugin.ts @@ -49,8 +49,8 @@ class IORedisPlugin implements SwPlugin { span.component = Component.REDIS; span.layer = SpanLayer.CACHE; span.peer = host; - span.tag(Tag.cacheType('Redis')); - span.tag(Tag.cacheInstance(`${this.condition.select}`)); + span.tag(Tag.dbType('Redis')); + span.tag(Tag.dbInstance(`${this.condition.select}`)); const ret = wrapPromise(span, _original.apply(this, args)); span.async(); diff --git a/tests/plugins/ioredis/expected.data.yaml b/tests/plugins/ioredis/expected.data.yaml index 151c2b1..d527352 100644 --- a/tests/plugins/ioredis/expected.data.yaml +++ b/tests/plugins/ioredis/expected.data.yaml @@ -34,8 +34,8 @@ segmentItems: skipAnalysis: false tags: - { key: coldStart, value: "true" } - - { key: cache.type, value: Redis } - - { key: cache.instance, value: "0" } + - { key: db.type, value: Redis } + - { key: db.instance, value: "0" } - segmentId: not null spans: - operationName: redis/set @@ -50,8 +50,8 @@ segmentItems: peer: redis:6379 skipAnalysis: false tags: - - { key: cache.type, value: Redis } - - { key: cache.instance, value: "0" } + - { key: db.type, value: Redis } + - { key: db.instance, value: "0" } - operationName: redis/get operationId: 0 parentSpanId: 0 @@ -64,8 +64,8 @@ segmentItems: peer: redis:6379 skipAnalysis: false tags: - - { key: cache.type, value: Redis } - - { key: cache.instance, value: "0" } + - { key: db.type, value: Redis } + - { key: db.instance, value: "0" } - operationName: /ioredis operationId: 0 parentSpanId: -1 From 82f0abbc7b69780a4ffaae30f704f168d7256508 Mon Sep 17 00:00:00 2001 From: tyk Date: Mon, 24 May 2021 10:52:53 +0800 Subject: [PATCH 13/13] modify readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7457ab6..a3395f3 100644 --- a/README.md +++ b/README.md @@ -79,11 +79,13 @@ Library | Plugin Name | [`Express`](https://expressjs.com) | `express` | | [`Axios`](https://github.com/axios/axios) | `axios` | | [`MySQL`](https://github.com/mysqljs/mysql) | `mysql` | +| [`MySQL`](https://github.com/sidorares/node-mysql2) | `mysql2` | | [`PostgreSQL`](https://github.com/brianc/node-postgres) | `pg` | | [`pg-cursor`](https://github.com/brianc/node-postgres) | `pg-cursor` | | [`MongoDB`](https://github.com/mongodb/node-mongodb-native) | `mongodb` | | [`Mongoose`](https://github.com/Automattic/mongoose) | `mongoose` | | [`RabbitMQ`](https://github.com/squaremo/amqp.node) | `amqplib` | +| [`Redis`](https://github.com/luin/ioredis) | `ioredis` | ### Compatible Libraries