diff --git a/CHANGELOG.md b/CHANGELOG.md index e24973d6..81a68ea3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Add support for injecting trace context into logs. + ([#121](https://github.com/signalfx/splunk-otel-js/pull/121)) + ## 0.8.0 (04-15-2021) - Added support for `aws-sdk`, `mongoose`, `sequelize`, `typeorm` and `kafkajs`. diff --git a/README.md b/README.md index 5e2eb454..52203b2f 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ You can also instrument your app with code as described [here](#instrument-with- | SPLUNK_ACCESS_TOKEN | acceessToken | | | The optional organization access token for trace submission requests. | | SPLUNK_MAX_ATTR_LENGTH | maxAttrLength | 1200 | Maximum length of string attribute value in characters. Longer values are truncated. | | SPLUNK_CONTEXT_SERVER_TIMING_ENABLED | serverTimingEnabled | true | Enable injection of `Server-Timing` header to HTTP responses. | +| SPLUNK_LOGS_INJECTION | logInjectionEnabled | false | Enable injecting of trace ID, span ID and service name to log records. Please note that the corresponding logging library instrumentation needs to be installed. | | OTEL_RESOURCE_ATTRIBUTES | | unset | Comma-separated list of resource attributes added to every reported span.
Example`key1=val1,key2=val2`
| OTEL_TRACE_ENABLED | | `true` | Globally enables tracer creation and auto-instrumentation. | @@ -158,6 +159,8 @@ startTracing({ - `serverTimingEnabled`: corresponds to the `SPLUNK_SERVER_TIMING_ENABLED` environment variable. Defaults to false. Enables injection of `Server-Timing` header to responses. +- `logInjectionEnabled`: corresponds to the `SPLUNK_LOGS_INJECTION` environment variable. Defaults to false. Injects trace ID, span ID and service name to the log records. Service version or deployment environment will be injected if available in the [configured resource](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/README.md). Supported logging libraries: bunyan, pino, winston. + - `tracerConfig`: a JS object that is merged into the default tracer config replacing any existing keys and is passed on to the tracer provider during initialization. This can be used to customize the tracer provider or tracer. Must satisfy [`TracerConfig` interface](https://github.com/open-telemetry/opentelemetry-js/blob/71ba83a0dc51118e08e3148c788b81fe711003e7/packages/opentelemetry-tracing/src/types.ts#L26) - `spanExporterFactory`: A function that accepts the options passed to startTracing function and returns a new instance of SpanExporter. When set, this function will be used to create a new exporter and the returned exporter will be used in the pipeline. @@ -221,6 +224,14 @@ opentelemetry-instrumentation-amqplib opentelemetry-instrumentation-elasticsearch ``` +If log injection is enabled, the corresponding logging library package will need to be installed beforehand. Supported logging library instrumentations: + +``` +@opentelemetry/instrumentation-bunyan +@opentelemetry/instrumentation-pino +@opentelemetry/instrumentation-winston +``` + You can find more instrumentation packages over at the [OpenTelemetry Registry](https://opentelemetry.io/registry/?language=js) and enable them manually as described above. diff --git a/package-lock.json b/package-lock.json index b45fa0db..b3e3eedd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -307,6 +307,17 @@ "to-fast-properties": "^2.0.0" } }, + "@dabh/diagnostics": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", + "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", + "dev": true, + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "@eslint/eslintrc": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.1.tgz", @@ -438,6 +449,16 @@ "shimmer": "^1.2.1" } }, + "@opentelemetry/instrumentation-bunyan": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.16.0.tgz", + "integrity": "sha512-AUVVQBhuYVRME/yZc8Ur/WrMEmOEqfq4KrMh6oGW+BgZzScXfhZjQrNyDyFtihmSpVS2J35J2OIsx0a3XKWqMw==", + "dev": true, + "requires": { + "@opentelemetry/api": "^1.0.0-rc.0", + "@opentelemetry/instrumentation": "^0.19.0" + } + }, "@opentelemetry/instrumentation-http": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.19.0.tgz", @@ -449,6 +470,27 @@ "semver": "^7.1.3" } }, + "@opentelemetry/instrumentation-pino": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.16.0.tgz", + "integrity": "sha512-tvxNcG/M683ZrobRLPj2hwqLDOjw4PiR2BISu4hh/wT7yx/9VUIR9wgHEFW+cPa3GFiya8r3K+j58tBhQpv+Yw==", + "dev": true, + "requires": { + "@opentelemetry/api": "^1.0.0-rc.0", + "@opentelemetry/instrumentation": "^0.19.0", + "semver": "^7.3.5" + } + }, + "@opentelemetry/instrumentation-winston": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.16.0.tgz", + "integrity": "sha512-UiqjuElocAcXxUVnBVhTpErb+KiA0FYfqy1dJyC+wUaRNybWO9G/T7Cd4UbjeXa5YeT/zcs1CBmhaORiqycIgw==", + "dev": true, + "requires": { + "@opentelemetry/api": "^1.0.0-rc.0", + "@opentelemetry/instrumentation": "^0.19.0" + } + }, "@opentelemetry/node": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/@opentelemetry/node/-/node-0.19.0.tgz", @@ -515,9 +557,9 @@ } }, "@sinonjs/fake-timers": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.0.tgz", - "integrity": "sha512-hAEzXi6Wbvlb67NnGMGSNOeAflLVnMa4yliPU/ty1qjgW/vAletH15/v/esJwASSIA0YlIyjnloenFbEZc9q9A==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", + "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", "dev": true, "requires": { "@sinonjs/commons": "^1.7.0" @@ -579,6 +621,15 @@ "integrity": "sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA==", "dev": true }, + "@types/bunyan": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.6.tgz", + "integrity": "sha512-YiozPOOsS6bIuz31ilYqR5SlLif4TBWsousN2aCWLi5233nZSX19tFbcQUPdR7xJ8ypPyxkCGNxg0CIV5n9qxQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/json-schema": { "version": "7.0.7", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", @@ -1005,6 +1056,18 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, + "async": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", + "dev": true + }, + "atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "dev": true + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1102,6 +1165,18 @@ "xtend": "^4.0.0" } }, + "bunyan": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.6.tgz", + "integrity": "sha1-aidize+RIpsBS/G28drWTZlQ8I8=", + "dev": true, + "requires": { + "dtrace-provider": "~0.8", + "moment": "^2.10.6", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, "cacheable-request": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", @@ -1314,6 +1389,16 @@ "urlgrey": "0.4.4" } }, + "color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "dev": true, + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1329,12 +1414,38 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "color-string": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", + "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "colorette": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", "dev": true }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true + }, + "colorspace": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", + "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", + "dev": true, + "requires": { + "color": "3.0.x", + "text-hex": "1.0.x" + } + }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -1378,6 +1489,12 @@ } } }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -1502,6 +1619,16 @@ "is-obj": "^2.0.0" } }, + "dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.14.0" + } + }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -1509,9 +1636,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.740", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.740.tgz", - "integrity": "sha512-Mi2m55JrX2BFbNZGKYR+2ItcGnR4O5HhrvgoRRyZQlaMGQULqDhoGkLWHzJoshSzi7k1PUofxcDbNhlFrDZNhg==", + "version": "1.3.741", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.741.tgz", + "integrity": "sha512-4i3T0cwnHo1O4Mnp9JniEco8bZiXoqbm3PhW5hv7uu8YLg35iajYrRnNyKFaN8/8SSTskU2hYqVTeYVPceSpUA==", "dev": true }, "emoji-regex": { @@ -1520,6 +1647,12 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -1858,6 +1991,18 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fast-redact": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.0.1.tgz", + "integrity": "sha512-kYpn4Y/valC9MdrISg47tZOpYBNoTXKgT9GYXFpHN/jYFs+lFkPoisY+LcBODdKVMY96ATzvzsWv+ES/4Kmufw==", + "dev": true + }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", + "dev": true + }, "fastq": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", @@ -1867,6 +2012,12 @@ "reusify": "^1.0.4" } }, + "fecha": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", + "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==", + "dev": true + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -1939,12 +2090,24 @@ "rimraf": "^3.0.2" } }, + "flatstr": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/flatstr/-/flatstr-1.0.12.tgz", + "integrity": "sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==", + "dev": true + }, "flatted": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", "dev": true }, + "fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true + }, "foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", @@ -2010,15 +2173,15 @@ "dev": true }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", "dev": true, + "optional": true, "requires": { - "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "2 || 3", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } @@ -2658,6 +2821,12 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, + "kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true + }, "latest-version": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", @@ -2736,6 +2905,19 @@ "chalk": "^4.0.0" } }, + "logform": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", + "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", + "dev": true, + "requires": { + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "triple-beam": "^1.3.0" + } + }, "long": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/long/-/long-2.4.0.tgz", @@ -3019,6 +3201,13 @@ "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", "integrity": "sha1-EUyUlnPiqKNenTV4hSeqN7Z52is=" }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "dev": true, + "optional": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -3030,6 +3219,37 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "dev": true, + "optional": true, + "requires": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + }, + "dependencies": { + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "dev": true, + "optional": true, + "requires": { + "glob": "^6.0.1" + } + } + } + }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "dev": true, + "optional": true + }, "nanoid": { "version": "3.1.20", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", @@ -3196,6 +3416,20 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -3259,6 +3493,15 @@ "wrappy": "1" } }, + "one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "requires": { + "fn.name": "1.x.x" + } + }, "onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -3429,6 +3672,26 @@ "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true }, + "pino": { + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/pino/-/pino-6.11.3.tgz", + "integrity": "sha512-drPtqkkSf0ufx2gaea3TryFiBHdNIdXKf5LN0hTM82SXI4xVIve2wLwNg92e1MT6m3jASLu6VO7eGY6+mmGeyw==", + "dev": true, + "requires": { + "fast-redact": "^3.0.0", + "fast-safe-stringify": "^2.0.7", + "flatstr": "^1.0.12", + "pino-std-serializers": "^3.1.0", + "quick-format-unescaped": "^4.0.3", + "sonic-boom": "^1.0.2" + } + }, + "pino-std-serializers": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz", + "integrity": "sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg==", + "dev": true + }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -3470,6 +3733,12 @@ "resolved": "https://registry.npmjs.org/process/-/process-0.10.1.tgz", "integrity": "sha1-hCRXzFHP7XLcd1r+6vuMYDQ3JyU=" }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, "process-on-spawn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", @@ -3516,6 +3785,12 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "quick-format-unescaped": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.3.tgz", + "integrity": "sha512-MaL/oqh02mhEo5m5J2rwsVL23Iw2PEaGVHgT2vFt8AAsr0lfvQA5dpXo9TPu0rz7tSBdUPgkbam0j/fj5ZM8yg==", + "dev": true + }, "quick-lru": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", @@ -3612,6 +3887,17 @@ "type-fest": "^0.8.1" } }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, "readdirp": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", @@ -3894,6 +4180,20 @@ "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", "dev": true }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "globals": { "version": "12.4.0", "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", @@ -4057,6 +4357,22 @@ "dev": true, "requires": { "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } } }, "run-async": { @@ -4089,6 +4405,13 @@ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, + "safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "dev": true, + "optional": true + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -4161,6 +4484,23 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + } + } + }, "sinon": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.1.tgz", @@ -4235,6 +4575,16 @@ } } }, + "sonic-boom": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz", + "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==", + "dev": true, + "requires": { + "atomic-sleep": "^1.0.0", + "flatstr": "^1.0.12" + } + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -4311,6 +4661,12 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true + }, "stream-events": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", @@ -4336,6 +4692,15 @@ "strip-ansi": "^6.0.0" } }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -4451,8 +4816,30 @@ "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" + }, + "dependencies": { + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } } }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -4511,6 +4898,12 @@ "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", "dev": true }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==", + "dev": true + }, "ts-mocha": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-8.0.0.tgz", @@ -4715,6 +5108,12 @@ "integrity": "sha1-iS/pWWCAXoVRnxzUOJ8stMu3ZS8=", "dev": true }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", @@ -4802,6 +5201,71 @@ "string-width": "^4.0.0" } }, + "winston": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", + "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", + "dev": true, + "requires": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.1.0", + "is-stream": "^2.0.0", + "logform": "^2.2.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.4.0" + } + }, + "winston-transport": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", + "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", + "dev": true, + "requires": { + "readable-stream": "^2.3.7", + "triple-beam": "^1.2.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/package.json b/package.json index 344abc77..7798310c 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,10 @@ }, "devDependencies": { "@opentelemetry/instrumentation-http": "~0.19.0", + "@opentelemetry/instrumentation-bunyan": "~0.16.0", + "@opentelemetry/instrumentation-pino": "~0.16.0", + "@opentelemetry/instrumentation-winston": "~0.16.0", + "@types/bunyan": "1.8.6", "@types/mocha": "8.2.2", "@types/node": "15.12.0", "@types/semver": "7.3.6", @@ -53,6 +57,7 @@ "@types/sinon": "^10.0.0", "@typescript-eslint/eslint-plugin": "^4.15.2", "@typescript-eslint/parser": "^4.15.2", + "bunyan": "1.8.6", "codecov": "3.8.2", "eslint": "^7.20.0", "eslint-plugin-header": "^3.1.1", @@ -64,11 +69,13 @@ "prettier": ">=1.13.0", "rewire": "^5.0.0", "rimraf": "3.0.2", + "pino": "^6.11.3", "shimmer": "1.2.1", "sinon": "^11.0.0", "ts-mocha": "8.0.0", "ts-node": "10.0.0", - "typescript": "4.3.2" + "typescript": "4.3.2", + "winston": "3.3.3" }, "dependencies": { "@opentelemetry/api": "~1.0.0-rc.3", @@ -79,6 +86,7 @@ "@opentelemetry/node": "~0.19.0", "@opentelemetry/propagator-b3": "~0.19.0", "@opentelemetry/resources": "~0.19.0", + "@opentelemetry/semantic-conventions": "~0.19.0", "@opentelemetry/tracing": "~0.19.0", "jaeger-client": "^3.15.0", "semver": "^7.1.3" @@ -97,6 +105,9 @@ "@opentelemetry/instrumentation-net": "~0.16.0", "@opentelemetry/instrumentation-pg": "~0.16.0", "@opentelemetry/instrumentation-redis": "~0.16.0", + "@opentelemetry/instrumentation-bunyan": "~0.16.0", + "@opentelemetry/instrumentation-pino": "~0.16.0", + "@opentelemetry/instrumentation-winston": "~0.16.0", "opentelemetry-instrumentation-amqplib": "~0.4.2", "opentelemetry-instrumentation-aws-sdk": "~0.4.3", "opentelemetry-instrumentation-elasticsearch": "~0.4.2", @@ -145,6 +156,15 @@ "@opentelemetry/instrumentation-hapi": { "optional": true }, + "@opentelemetry/instrumentation-bunyan": { + "optional": true + }, + "@opentelemetry/instrumentation-pino": { + "optional": true + }, + "@opentelemetry/instrumentation-winston": { + "optional": true + }, "opentelemetry-instrumentation-amqplib": { "optional": true }, diff --git a/src/instrumentations/index.ts b/src/instrumentations/index.ts index 5167457b..031f4bc8 100644 --- a/src/instrumentations/index.ts +++ b/src/instrumentations/index.ts @@ -31,6 +31,9 @@ const supportedInstrumentations: [string, string][] = [ ['@opentelemetry/instrumentation-net', 'NetInstrumentation'], ['@opentelemetry/instrumentation-pg', 'PgInstrumentation'], ['@opentelemetry/instrumentation-hapi', 'HapiInstrumentation'], + ['@opentelemetry/instrumentation-bunyan', 'BunyanInstrumentation'], + ['@opentelemetry/instrumentation-pino', 'PinoInstrumentation'], + ['@opentelemetry/instrumentation-winston', 'WinstonInstrumentation'], ['opentelemetry-instrumentation-amqplib', 'AmqplibInstrumentation'], [ 'opentelemetry-instrumentation-elasticsearch', diff --git a/src/instrumentations/logging.ts b/src/instrumentations/logging.ts new file mode 100644 index 00000000..52d442c5 --- /dev/null +++ b/src/instrumentations/logging.ts @@ -0,0 +1,72 @@ +/* + * Copyright Splunk Inc. + * + * Licensed 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 { Options } from '../options'; +import { Span } from '@opentelemetry/tracing'; +import { ResourceAttributes } from '@opentelemetry/semantic-conventions'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type LogRecord = Record; + +export function configureLogInjection( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + instrumentation: any, + options: Options +) { + if (!options.logInjectionEnabled) { + return; + } + + if ( + typeof instrumentation['setConfig'] !== 'function' || + typeof instrumentation['getConfig'] !== 'function' + ) { + return; + } + + const logHook = (span: Span, record: LogRecord) => { + record['service.name'] = + span.resource.attributes[ResourceAttributes.SERVICE_NAME]; + + const version = + span.resource.attributes[ResourceAttributes.SERVICE_VERSION]; + if (version !== undefined) { + record['service.version'] = version; + } + + const environment = + span.resource.attributes[ResourceAttributes.DEPLOYMENT_ENVIRONMENT]; + if (environment !== undefined) { + record['service.environment'] = environment; + } + }; + + let config = instrumentation.getConfig(); + + if (config === undefined) { + config = { logHook }; + } else if (config.logHook !== undefined) { + const original = config.logHook; + config.logHook = function (this: unknown, span: Span, record: LogRecord) { + logHook(span, record); + original.call(this, span, record); + }; + } else { + config.logHook = logHook; + } + + instrumentation.setConfig(config); +} diff --git a/src/options.ts b/src/options.ts index 9bb7611f..d658baa1 100644 --- a/src/options.ts +++ b/src/options.ts @@ -27,6 +27,8 @@ import { getInstrumentations } from './instrumentations'; import { JaegerExporter } from '@opentelemetry/exporter-jaeger'; import { EnvResourceDetector } from './resource'; import { NodeTracerConfig } from '@opentelemetry/node'; +import { Resource } from '@opentelemetry/resources'; +import { ResourceAttributes } from '@opentelemetry/semantic-conventions'; import { TextMapPropagator } from '@opentelemetry/api'; import { CompositePropagator, HttpTraceContext } from '@opentelemetry/core'; @@ -48,6 +50,7 @@ export interface Options { accessToken: string; maxAttrLength: number; serverTimingEnabled: boolean; + logInjectionEnabled: boolean; instrumentations: InstrumentationOption[]; tracerConfig: NodeTracerConfig; spanExporterFactory: SpanExporterFactory; @@ -75,14 +78,29 @@ export function _setDefaultOptions(options: Partial = {}): Options { ); } - options.serviceName = - options.serviceName || env.SPLUNK_SERVICE_NAME || defaultServiceName; + if (options.logInjectionEnabled === undefined) { + options.logInjectionEnabled = getEnvBoolean('SPLUNK_LOGS_INJECTION', false); + } + options.endpoint = options.endpoint || env.OTEL_EXPORTER_JAEGER_ENDPOINT || defaultEndpoint; const extraTracerConfig = options.tracerConfig || {}; + + const resource = new EnvResourceDetector().detect(); + + options.serviceName = + options.serviceName || + env.SPLUNK_SERVICE_NAME || + resource.attributes[ResourceAttributes.SERVICE_NAME]?.toString() || + defaultServiceName; + const tracerConfig = { - resource: new EnvResourceDetector().detect(), + resource: resource.merge( + new Resource({ + [ResourceAttributes.SERVICE_NAME]: options.serviceName, + }) + ), ...extraTracerConfig, }; @@ -105,6 +123,7 @@ export function _setDefaultOptions(options: Partial = {}): Options { accessToken: options.accessToken, maxAttrLength: options.maxAttrLength, serverTimingEnabled: options.serverTimingEnabled, + logInjectionEnabled: options.logInjectionEnabled, instrumentations: options.instrumentations, tracerConfig: tracerConfig, spanExporterFactory: options.spanExporterFactory, diff --git a/src/tracing.ts b/src/tracing.ts index 4363326f..4988f0c9 100644 --- a/src/tracing.ts +++ b/src/tracing.ts @@ -19,6 +19,7 @@ import { NodeTracerProvider } from '@opentelemetry/node'; import { registerInstrumentations } from '@opentelemetry/instrumentation'; import { configureHttpInstrumentation } from './instrumentations/http'; +import { configureLogInjection } from './instrumentations/logging'; import { Options, _setDefaultOptions } from './options'; import { _patchJaeger } from './jaeger'; import { gte } from 'semver'; @@ -76,10 +77,16 @@ function configureInstrumentations(options: Options) { for (const instrumentation of options.instrumentations) { // eslint-disable-next-line @typescript-eslint/no-explicit-any const instr = instrumentation as any; - if ( - instr['instrumentationName'] === '@opentelemetry/instrumentation-http' - ) { - configureHttpInstrumentation(instr, options); + + switch (instr['instrumentationName']) { + case '@opentelemetry/instrumentation-http': + configureHttpInstrumentation(instr, options); + break; + case '@opentelemetry/instrumentation-bunyan': + case '@opentelemetry/instrumentation-pino': + case '@opentelemetry/instrumentation-winston': + configureLogInjection(instr, options); + break; } } } diff --git a/test/instrumentations.test.ts b/test/instrumentations.test.ts index d53b2bf0..7582a75c 100644 --- a/test/instrumentations.test.ts +++ b/test/instrumentations.test.ts @@ -35,6 +35,9 @@ describe('instrumentations', () => { ['@opentelemetry/instrumentation-net', 'NetInstrumentation'], ['@opentelemetry/instrumentation-pg', 'PgInstrumentation'], ['@opentelemetry/instrumentation-hapi', 'HapiInstrumentation'], + ['@opentelemetry/instrumentation-bunyan', 'BunyanInstrumentation'], + ['@opentelemetry/instrumentation-pino', 'PinoInstrumentation'], + ['@opentelemetry/instrumentation-winston', 'WinstonInstrumentation'], ['opentelemetry-instrumentation-amqplib', 'AmqplibInstrumentation'], [ 'opentelemetry-instrumentation-elasticsearch', @@ -49,7 +52,8 @@ describe('instrumentations', () => { it('does not load if packages are not installed', () => { const inst = instrumentations.getInstrumentations(); - assert.strictEqual(inst.length, 1); + // Note: the number here is the devDependencies instrumentation count. + assert.strictEqual(inst.length, 4); }); it('load instrumentations if they are not installed', () => { diff --git a/test/loginjection.test.ts b/test/loginjection.test.ts new file mode 100644 index 00000000..95f22fa2 --- /dev/null +++ b/test/loginjection.test.ts @@ -0,0 +1,104 @@ +/* + * Copyright Splunk Inc. + * + * Licensed 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 assert from 'assert'; +import { Writable } from 'stream'; +import { context, trace, getSpan, setSpan } from '@opentelemetry/api'; +import { startTracing } from '../src/tracing'; +import type * as pino from 'pino'; +import type * as bunyan from 'bunyan'; +import type * as winston from 'winston'; + +describe('log injection', () => { + let stream: Writable; + let record: any; + + function assertInjection(logger, done, extra) { + const span = trace.getTracer('test').startSpan('main'); + context.with(setSpan(context.active(), span), () => { + const { traceId, spanId } = span.context(); + logger.info('my-log-message'); + assert.strictEqual(record['trace_id'], traceId); + assert.strictEqual(record['span_id'], spanId); + assert.strictEqual(record['service.name'], 'test-service'); + + for (const [key, value] of extra || []) { + assert.strictEqual(record[key], value); + } + + done(); + }); + } + + before(() => { + startTracing({ logInjectionEnabled: true, serviceName: 'test-service' }); + }); + + beforeEach(() => { + stream = new Writable({ + write: chunk => { + record = JSON.parse(chunk); + }, + }); + record = {}; + }); + + it('injects context to bunyan records', done => { + const logger: bunyan = require('bunyan').createLogger({ + name: 'test', + stream, + }); + assertInjection(logger, done); + }); + + it('injects context to pino records', done => { + const logger: pino = require('pino')(stream); + assertInjection(logger, done); + }); + + it('injects context to winston records', done => { + const winston: winston = require('winston'); + const logger = winston.createLogger({ + transports: [new winston.transports.Stream({ stream })], + }); + assertInjection(logger, done); + }); + + describe('injecting version and environment', () => { + before(() => { + process.env.OTEL_RESOURCE_ATTRIBUTES = + 'service.version=1,deployment.environment=test'; + }); + + after(() => { + delete process.env.OTEL_RESOURCE_ATTRIBUTES; + }); + + it('injects service version and service environment if available', done => { + startTracing({ logInjectionEnabled: true, serviceName: 'test-service' }); + + const logger: bunyan = require('bunyan').createLogger({ + name: 'test', + stream, + }); + + assertInjection(logger, done, [ + ['service.version', '1'], + ['service.environment', 'test'], + ]); + }); + }); +}); diff --git a/test/options.test.ts b/test/options.test.ts index 77f5845f..b2758b84 100644 --- a/test/options.test.ts +++ b/test/options.test.ts @@ -18,6 +18,7 @@ import { TextMapGetter, TextMapPropagator } from '@opentelemetry/api'; import { HttpBaggage } from '@opentelemetry/core'; import { InstrumentationBase } from '@opentelemetry/instrumentation'; import { Resource } from '@opentelemetry/resources'; +import { ResourceAttributes } from '@opentelemetry/semantic-conventions'; import { SimpleSpanProcessor, SpanExporter, @@ -49,10 +50,13 @@ describe('options', () => { serviceName: 'unnamed-node-service', accessToken: '', serverTimingEnabled: true, + logInjectionEnabled: false, maxAttrLength: 1200, instrumentations: [], tracerConfig: { - resource: new Resource({}), + resource: new Resource({ + [ResourceAttributes.SERVICE_NAME]: 'unnamed-node-service', + }), }, spanExporterFactory: defaultSpanExporterFactory, spanProcessorFactory: defaultSpanProcessorFactory, @@ -70,6 +74,7 @@ describe('options', () => { serviceName: 'custom-service-name', accessToken: 'custom-access-token', maxAttrLength: 4000, + logInjectionEnabled: true, instrumentations: [testInstrumentation], tracerConfig: { resource: new Resource({ @@ -88,6 +93,7 @@ describe('options', () => { accessToken: 'custom-access-token', maxAttrLength: 4000, serverTimingEnabled: true, + logInjectionEnabled: true, instrumentations: [testInstrumentation], tracerConfig: { resource: new Resource({ attr1: 'value' }), @@ -98,6 +104,18 @@ describe('options', () => { propagatorFactory: testPropagatorFactory, }); }); + + it('prefers service name from env resource info over the default service name', () => { + process.env.OTEL_RESOURCE_ATTRIBUTES = 'service.name=foobar'; + const options = _setDefaultOptions(); + delete process.env.OTEL_RESOURCE_ATTRIBUTES; + + assert.deepStrictEqual(options.tracerConfig, { + resource: new Resource({ + [ResourceAttributes.SERVICE_NAME]: 'foobar', + }), + }); + }); }); class TestInstrumentation extends InstrumentationBase { diff --git a/test/tracing.test.ts b/test/tracing.test.ts index 7065b94f..259363e1 100644 --- a/test/tracing.test.ts +++ b/test/tracing.test.ts @@ -55,7 +55,8 @@ describe('tracing', () => { exportURL: string, serviceName: string, accessToken?: string, - maxAttrLength?: number + maxAttrLength?: number, + logsInjection: boolean ) { sinon.assert.calledOnce(addSpanProcessorMock); const processor = addSpanProcessorMock.getCall(0).args[0]; @@ -91,7 +92,8 @@ describe('tracing', () => { 'http://localhost:9080/v1/trace', 'unnamed-node-service', '', - 1200 + 1200, + false ); }); @@ -100,8 +102,21 @@ describe('tracing', () => { const serviceName = 'test-node-service'; const accessToken = '1234'; const maxAttrLength = 50; - startTracing({ endpoint, serviceName, accessToken, maxAttrLength }); - assertTracingPipeline(endpoint, serviceName, accessToken, maxAttrLength); + const logInjectionEnabled = true; + startTracing({ + endpoint, + serviceName, + accessToken, + maxAttrLength, + logInjectionEnabled, + }); + assertTracingPipeline( + endpoint, + serviceName, + accessToken, + maxAttrLength, + logInjectionEnabled + ); }); it('setups tracing with custom options from env', () => { @@ -109,11 +124,13 @@ describe('tracing', () => { const serviceName = 'env-service'; const accessToken = 'zxcvb'; const maxAttrLength = 101; + const logInjectionEnabled = true; process.env.OTEL_EXPORTER_JAEGER_ENDPOINT = ''; process.env.SPLUNK_SERVICE_NAME = ''; process.env.SPLUNK_ACCESS_TOKEN = ''; process.env.SPLUNK_MAX_ATTR_LENGTH = '42'; + process.env.SPLUNK_LOGS_INJECTION = 'true'; const envExporterStub = sinon .stub(process.env, 'OTEL_EXPORTER_JAEGER_ENDPOINT') .value(url); @@ -126,13 +143,23 @@ describe('tracing', () => { const envMaxAttrLength = sinon .stub(process.env, 'SPLUNK_MAX_ATTR_LENGTH') .value(maxAttrLength); + const envLogsInjection = sinon + .stub(process.env, 'SPLUNK_LOGS_INJECTION') + .value(logInjectionEnabled); startTracing(); - assertTracingPipeline(url, serviceName, accessToken, maxAttrLength); + assertTracingPipeline( + url, + serviceName, + accessToken, + maxAttrLength, + logInjectionEnabled + ); envExporterStub.restore(); envServiceStub.restore(); envAccessStub.restore(); envMaxAttrLength.restore(); + envLogsInjection.restore(); }); it('setups tracing with multiple processors', () => {