Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to universal context local storage #14

Merged
merged 32 commits into from
Nov 13, 2017
Merged

Migrate to universal context local storage #14

merged 32 commits into from
Nov 13, 2017

Conversation

pglombardo
Copy link

@pglombardo pglombardo commented Nov 2, 2017

This PR updates the sensor to use the cls-hooked package which supports both older and the most recent versions of Node.

  • Use cls-hooked
  • Add tests to validate CLS
  • Remove/update boot time checks
  • Update instrumentation
    • http.Server
    • http.Client
    • Elastic
    • Kafka
    • MySQL
    • Mongo
  • Update/add to tests
  • ES6 Lint decision on function() {} vs () => {}
  • My local Kafka is broken; need to fix and finish Kafka instrumentation validation

- Unified context & span concepts under 'span' only
  - Span creation now done in cls module; less/more centralized/consistent code
  - Instrumentation code is now more concise
  - No longer need to track the destroy of old uid/contexts
  - `cls.startSpan(spanName, traceId, spanId)` - start new span in this context
  - `cls.getCurrentSpan()` - get current in this context
  - `cls.isTracing()` - returns boolean if we have a current span
- Update instrumentation to use new exports
- Revamp cls tests
- Store per request tracing level in request context (with cls exports to access)
  - `cls.setTracingLevel(level)` - to set the level
  - `cls.tracingLevel()` - to access the level
  - `cls.tracingSuppressed()` - boolean return; false if 0; true otherwise
- Keep a registry of exit span types and offer function to query spans
  - `cls.isExitSpan(span)` - boolean return if this span is an exit span
@pglombardo
Copy link
Author

pglombardo commented Nov 12, 2017

PR summary in 6caa0ca but I'll re-post here:

Cleaner, leaner & meaner CLS implementation

  • Unified context & span concepts under 'span' only
    • Span creation now done in cls module
    • More centralized/consistent code
    • Instrumentation code is now more concise
    • No longer need to track the destroy of old uid/contexts
    • cls.startSpan(spanName, traceId, spanId) - start new span in this context
    • cls.getCurrentSpan() - get current span in this context
    • cls.isTracing() - returns boolean if we have a current span
  • Update instrumentation to use new exports
  • Revamp cls tests
  • Store per request tracing level in request context (with cls exports to access)
    • cls.setTracingLevel(level) - to set the level
    • cls.tracingLevel() - to access the level
    • cls.tracingSuppressed() - boolean return; false if 0; true otherwise
  • Keep a registry of exit span types and offer function to query spans
    • cls.isExitSpan(span) - boolean return if this span is an exit span

Copy link
Contributor

@bripkens bripkens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Damn @pglombardo, this is looking really, really good! I just have a bunch of small remarks.

.eslintrc Outdated
env:
node: true
mocha: true
es6: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We aren't using any es6 features besides lambdas and retaining some basic Node.js 0.12 support is also good. Hence I would say let's please disable es6

.eslintrc Outdated
@@ -1,16 +1,29 @@
---
extends: airbnb/legacy
env:
node: true
mocha: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enabling mocha globally has unintended side effects, e.g. it registering some globals that we do not want. Please disable it.

.eslintrc Outdated
rules:
no-shadow: 0
require-yield: 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you disable all these rules?

*/
var exitSpans = ['node.http.client', 'elasticsearch', 'mongo', 'mysql'];
exports.isExitSpan = function isExitSpan(span) {
return (exitSpans.indexOf(span.n) > -1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we start reporting the k field as part of the span? Values would be:

  • 1: Entry
  • 2: Exit
  • 3: Intermediate, local

This check would then be really simple :).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if I understand this @bripkens. We have a type field for SDK spans but a k field?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 08e90c5

@@ -21,7 +21,7 @@ exports.init = function(_config) {
require('./opentracing').init(config, automaticTracingEnabled);

if (automaticTracingEnabled) {
require('./hook').init(config);
require('./cls');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it required if it doesn't need any initialization? As far as I can tell, requiring the module has no side effects, so this line can be removed.

@@ -97,7 +83,7 @@ function getExistingSpanId(req, fallback) {
fallback = arguments.length > 1 ? fallback : null;

var spanId = req.headers[tracingConstants.spanIdHeaderNameLowerCase];
if (spanId == null) {
if (spanId === null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking with eqeqeq will not respect undefined. Was this intentional?

@@ -109,7 +95,7 @@ function getExistingTraceId(req, fallback) {
fallback = arguments.length > 1 ? fallback : null;

var traceId = req.headers[tracingConstants.traceIdHeaderNameLowerCase];
if (traceId == null) {
if (traceId === null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking with eqeqeq will not respect undefined. Was this intentional?

clientRequest.setHeader(tracingConstants.spanIdHeaderName, span.s);
clientRequest.setHeader(tracingConstants.traceIdHeaderName, span.t);
clientRequest.setHeader(tracingConstants.traceLevelHeaderName, '1');
cls.ns.run(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should avoid ES6 features for the sake of supporting Node.js 0.12 as mentioned above

};

function shimEmit(realEmit) {
return function(type, req, res) {
return cls.ns.runAndReturn(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should avoid ES6 features for the sake of supporting Node.js 0.12 as mentioned above

access: 'consume',
service: message.topic
}
cls.ns.runAndReturn(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should avoid ES6 features for the sake of supporting Node.js 0.12 as mentioned above

var hooked = require('cls-hooked');
var currentSpanKey = 'csKey';

var exitSpans = ['node.http.client', 'elasticsearch', 'mongo', 'mysql'];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My intention was that instrumentations will set it themselves and that we therefore do not need such a list. Is this not possible?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it this way in Ruby which is why I made an assumption. I suppose we could instead add the span.k assignment in each instrumentation code directly but the intention was to simplify instrumentation by moving all the span creation details into CLS.

If you have a strong preference I can change it. Otherwise let's leave it and I'll revisit it on my next run through the code. lmk

@bripkens bripkens merged commit 5dbf7ab into master Nov 13, 2017
@bripkens bripkens deleted the cls branch November 13, 2017 15:57
basti1302 added a commit that referenced this pull request Nov 30, 2020
Here are two coredumps that were were created on CircleCI under Node.js 8:

(lldb) bt
* thread #1: tid = 0, 0x0000000000915192 node`node::http2::Http2Session::ClearOutgoing(int) + 178, name = 'node', stop reason = signal SIGSEGV
  * frame #0: 0x0000000000915192 node`node::http2::Http2Session::ClearOutgoing(int) + 178
    frame #1: 0x0000000000916047 node`node::http2::Http2Session::OnStreamAfterWriteImpl(node::WriteWrap*, int, void*) + 39
    frame #2: 0x0000000000950ee0 node`node::StreamBase::AfterWrite(node::WriteWrap*, int) + 160
    frame #3: 0x000000000099f161 node`node::TLSWrap::InvokeQueued(int, char const*) + 193
    frame #4: 0x00000000009a01a8 node`node::TLSWrap::EncOut() + 232
    frame #5: 0x00000000009a0bfd node`node::TLSWrap::OnAfterWriteImpl(node::WriteWrap*, int, void*) + 125
    frame #6: 0x0000000000950ee0 node`node::StreamBase::AfterWrite(node::WriteWrap*, int) + 160
    frame #7: 0x000000000095517b node`node::LibuvStreamWrap::AfterWrite(node::WriteWrap*, int) + 27
    frame #8: 0x0000000000955dc4 node`node::LibuvStreamWrap::AfterUvWrite(uv_write_s*, int) + 84
    frame #9: 0x00000000009c5129 node`uv__write_callbacks(stream=0x00000000041b05b8) + 265 at stream.c:976
    frame #10: 0x00000000009c6358 node`uv__stream_destroy(stream=0x00000000041b05b8) + 200 at stream.c:461
    frame #11: 0x00000000009bb34a node`uv_run + 8 at core.c:272
    frame #12: 0x00000000009bb342 node`uv_run + 277 at core.c:302
    frame #13: 0x00000000009bb22d node`uv_run(loop=0x000000000218cda0, mode=UV_RUN_DEFAULT) + 413 at core.c:372
    frame #14: 0x00000000008d6b65 node`node::Start(uv_loop_s*, int, char const* const*, int, char const* const*) + 1205
    frame #15: 0x00000000008d5da0 node`node::Start(int, char**) + 352
    frame #16: 0x00007fd77d7ed2e1 libc.so.6`__libc_start_main + 241
    frame #17: 0x000000000089f601 node`_start + 41

(lldb) bt
* thread #1: tid = 0, 0x0000000002c198e0, name = 'node', stop reason = signal SIGSEGV
  * frame #0: 0x0000000002c198e0
    frame #1: 0x0000000000916047 node`node::http2::Http2Session::OnStreamAfterWriteImpl(node::WriteWrap*, int, void*) + 39
    frame #2: 0x0000000000950ee0 node`node::StreamBase::AfterWrite(node::WriteWrap*, int) + 160
    frame #3: 0x000000000099f161 node`node::TLSWrap::InvokeQueued(int, char const*) + 193
    frame #4: 0x00000000009a01a8 node`node::TLSWrap::EncOut() + 232
    frame #5: 0x00000000009a0bfd node`node::TLSWrap::OnAfterWriteImpl(node::WriteWrap*, int, void*) + 125
    frame #6: 0x0000000000950ee0 node`node::StreamBase::AfterWrite(node::WriteWrap*, int) + 160
    frame #7: 0x000000000095517b node`node::LibuvStreamWrap::AfterWrite(node::WriteWrap*, int) + 27
    frame #8: 0x0000000000955dc4 node`node::LibuvStreamWrap::AfterUvWrite(uv_write_s*, int) + 84
    frame #9: 0x00000000009c5129 node`uv__write_callbacks(stream=0x0000000002d70078) + 265 at stream.c:976
    frame #10: 0x00000000009c6358 node`uv__stream_destroy(stream=0x0000000002d70078) + 200 at stream.c:461
    frame #11: 0x00000000009bb34a node`uv_run + 8 at core.c:272
    frame #12: 0x00000000009bb342 node`uv_run + 277 at core.c:302
    frame #13: 0x00000000009bb22d node`uv_run(loop=0x000000000218cda0, mode=UV_RUN_DEFAULT) + 413 at core.c:372
    frame #14: 0x00000000008d6b65 node`node::Start(uv_loop_s*, int, char const* const*, int, char const* const*) + 1205
    frame #15: 0x00000000008d5da0 node`node::Start(int, char**) + 352
    frame #16: 0x00007fde4be1a2e1 libc.so.6`__libc_start_main + 241
    frame #17: 0x000000000089f601 node`_start + 41
willianpc added a commit that referenced this pull request Apr 6, 2021
# This is the 1st commit message:

Added basic test apps

# This is the commit message #2:

improved test app a little bit

# This is the commit message #3:

more refactoring; wip: process, concurrency named process in test app

# This is the commit message #4:

added consumer as process

# This is the commit message #5:

wip: in the middle of another refactoring

# This is the commit message #6:

test app covers all 12 cases

# This is the commit message #7:

started instrumentation

# This is the commit message #8:

wip: instrumenting add() function

# This is the commit message #9:

wip: dealing with repeatable jobs

# This is the commit message #10:

Using repeat with limit. avoid using cron

# This is the commit message #11:

added bulk job sending

# This is the commit message #12:

small refactoring in sender test app

# This is the commit message #13:

exit span instrumented for single and repeatable jobs

# This is the commit message #14:

wip: bulk immediate jobs

[skip ci]

# This is the commit message #15:

wip: testing the instrumentation of bulk > send job

[skip ci]

# This is the commit message #16:

complete instrumentation of exit spans

# This is the commit message #17:

docs(changelog): prepare release 1.117.0

[skip ci]

# This is the commit message #18:

v1.117.0

# This is the commit message #19:

fix: bypass native addon loading in worker threads

Co-authored-by: Willian Carvalho <willian.carvalho@instana.com>
# This is the commit message #20:

docs(changelog): prepare release 1.117.1

[skip ci]

# This is the commit message #21:

v1.117.1

# This is the commit message #22:

test(tracing): make batching test less flaky

# This is the commit message #23:

chore: add script for splitting CI output according to package

[skip ci]

# This is the commit message #24:

fix: update to shimmer@1.2.1 to be able to patch async functions

Specifically, that version includes this fix:
othiym23/shimmer@ec15ba2

# This is the commit message #25:

test(tracing): fix path for custom tags

# This is the commit message #26:

wip: added ENTRY span instrumentation

# This is the commit message #27:

renamed child process.js; removed extra execTime param from /POST; added client HTTP call

# This is the commit message #28:

immediate and repeatable jobs instrumented correctly

[skip ci]

# This is the commit message #29:

small refactoring/cleanup; [skip ci]

# This is the commit message #30:

introduced tests

[skip ci]

# This is the commit message #31:

Included test for Callback x Bulk [skip ci]

# This is the commit message #32:

wip: attempting to instrument master.js [skip ci]

# This is the commit message #33:

instrument child process with an extra entry span

# This is the commit message #34:

experimental: activate tracing immediately, without waiting for connection to agent

# This is the commit message #35:

send spans directly from Bull child process workers and wait for them to be offloaded

# This is the commit message #36:

fix linting issues

# This is the commit message #37:

Updating agent uuid from env var info

[skip ci]

# This is the commit message #38:

fixed PID casting to string [skip ci]

# This is the commit message #39:

no more IPC, write to a file instead

# This is the commit message #40:

attach trace context to each individual IPC message instead of env var at process start

# This is the commit message #41:

avoid duplicated bull entry span

# This is the commit message #42:

fixed naming convention; replaced fs/promises by fs; guarantee that opts object exists

# This is the commit message #43:

added disabled trace tests

# This is the commit message #44:

lift the requirement for applying span.disableAutoEnd/span.end()

# This is the commit message #45:

Running tests for trace disabled and suppressed

# This is the commit message #46:

all instana data is removed before customer gets the processed job data

# This is the commit message #47:

Added test to make sure no Instana data is left in Job data

# This is the commit message #48:

Added withError cases

# This is the commit message #49:

Skipping tests for Node version < 10

# This is the commit message #50:

fix(opentracing): default to type entry when no parent is referenced

# This is the commit message #51:

docs(changelog): prepare release 1.117.2

[skip ci]

# This is the commit message #52:

v1.117.2

# This is the commit message #53:

fix(metrics): register gc stats loader listener immediately

This fixes an issue where GC metrics would not be available when the
dependency gcstats.js was already present in node_modules and the
activation of the gc metrics module would happen too late.

# This is the commit message #54:

docs(changelog): prepare release 1.117.3

[skip ci]

# This is the commit message #55:

v1.117.3

# This is the commit message #56:

docs(contributing): document the need to install PostgreSQL headers

(Only required for local development.)

Plus: Additional info about rate limited OTP when publishing.

[skip ci]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants