-
Notifications
You must be signed in to change notification settings - Fork 237
feat: zero-configuration support by always successfully inferring a serviceName #2410
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,17 +26,12 @@ var version = require('../package').version | |
| module.exports = Agent | ||
|
|
||
| function Agent () { | ||
| this.middleware = { connect: connect.bind(this) } | ||
| // Early configuration to ensure `agent.logger` works before `agent.start()`. | ||
| this.logger = config.configLogger() | ||
|
|
||
| this.logger = null | ||
| this._conf = null | ||
| this._httpClient = null | ||
| this._uncaughtExceptionListener = null | ||
|
|
||
| // Early configuration to ensure `agent.logger` works before `.start()` | ||
| // is called. | ||
| this._config() | ||
|
|
||
| this._inflightEvents = new InflightEventSet() | ||
| this._instrumentation = new Instrumentation(this) | ||
| this._metrics = new Metrics(this) | ||
|
|
@@ -46,6 +41,7 @@ function Agent () { | |
| this._transport = null | ||
|
|
||
| this.lambda = elasticApmAwsLambda(this) | ||
| this.middleware = { connect: connect.bind(this) } | ||
| } | ||
|
|
||
| Object.defineProperty(Agent.prototype, 'currentTransaction', { | ||
|
|
@@ -113,7 +109,7 @@ Agent.prototype.destroy = function () { | |
| // for tests that may use multiple Agent instances in a single test process. | ||
| global[symbols.agentInitialized] = null | ||
|
|
||
| if (Error.stackTraceLimit === this._conf.stackTraceLimit) { | ||
| if (this._origStackTraceLimit && Error.stackTraceLimit !== this._origStackTraceLimit) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. REVIEW NOTE: This is a guard against agent.destroy() being called before agent.start() |
||
| Error.stackTraceLimit = this._origStackTraceLimit | ||
| } | ||
| } | ||
|
|
@@ -127,7 +123,7 @@ Agent.prototype._getStats = function () { | |
| const stats = { | ||
| frameCache: frameCacheStats | ||
| } | ||
| if (typeof this._transport._getStats === 'function') { | ||
| if (this._transport && typeof this._transport._getStats === 'function') { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. REVIEW NOTE: This is a guard against agent._getStats() being called before agent.start() |
||
| stats.apmclient = this._transport._getStats() | ||
| } | ||
| return stats | ||
|
|
@@ -186,7 +182,7 @@ Agent.prototype.setSpanOutcome = function (outcome) { | |
| } | ||
|
|
||
| Agent.prototype._config = function (opts) { | ||
| this._conf = config(opts) | ||
| this._conf = config.createConfig(opts, this.logger) | ||
| this.logger = this._conf.logger | ||
|
|
||
| const { host, port, protocol } = this._conf.serverUrl | ||
|
|
@@ -204,7 +200,9 @@ Agent.prototype.isStarted = function () { | |
| } | ||
|
|
||
| Agent.prototype.start = function (opts) { | ||
| if (this.isStarted()) throw new Error('Do not call .start() more than once') | ||
| if (this.isStarted()) { | ||
| throw new Error('Do not call .start() more than once') | ||
| } | ||
| global[symbols.agentInitialized] = true | ||
|
|
||
| this._config(opts) | ||
|
|
@@ -217,15 +215,11 @@ Agent.prototype.start = function (opts) { | |
| this.logger.debug('Elastic APM agent disabled (`active` is false)') | ||
| return this | ||
| } else if (!this._conf.serviceName) { | ||
| this.logger.error('Elastic APM isn\'t correctly configured: Missing serviceName') | ||
| this._conf.active = false | ||
| return this | ||
| } else if (!/^[a-zA-Z0-9 _-]+$/.test(this._conf.serviceName)) { | ||
| this.logger.error('Elastic APM isn\'t correctly configured: serviceName "%s" contains invalid characters! (allowed: a-z, A-Z, 0-9, _, -, <space>)', this._conf.serviceName) | ||
| this.logger.error('Elastic APM is incorrectly configured: Missing serviceName (APM will be disabled)') | ||
| this._conf.active = false | ||
| return this | ||
| } else if (!(this._conf.serverPort >= 1 && this._conf.serverPort <= 65535)) { | ||
| this.logger.error('Elastic APM isn\'t correctly configured: serverUrl "%s" contains an invalid port! (allowed: 1-65535)', this._conf.serverUrl) | ||
| this.logger.error('Elastic APM is incorrectly configured: serverUrl "%s" contains an invalid port! (allowed: 1-65535)', this._conf.serverUrl) | ||
| this._conf.active = false | ||
| return this | ||
| } else if (this._conf.logLevel === 'trace') { | ||
|
|
@@ -266,11 +260,13 @@ Agent.prototype.start = function (opts) { | |
| } | ||
|
|
||
| Agent.prototype.getServiceName = function () { | ||
| return this._conf.serviceName | ||
| return this._conf ? this._conf.serviceName : undefined | ||
| } | ||
|
|
||
| Agent.prototype.setFramework = function ({ name, version, overwrite = true }) { | ||
| if (!this._transport || !this._conf) return | ||
| if (!this._transport || !this._conf) { | ||
| return | ||
| } | ||
| const conf = {} | ||
| if (name && (overwrite || !this._conf.frameworkName)) this._conf.frameworkName = conf.frameworkName = name | ||
| if (version && (overwrite || !this._conf.frameworkVersion)) this._conf.frameworkVersion = conf.frameworkVersion = version | ||
|
|
@@ -408,6 +404,13 @@ Agent.prototype.captureError = function (err, opts, cb) { | |
|
|
||
| const id = errors.generateErrorId() | ||
|
|
||
| if (!this.isStarted()) { | ||
| if (cb) { | ||
| cb(new Error('cannot capture error before agent is started'), id) | ||
| } | ||
| return | ||
| } | ||
|
|
||
| // Avoid unneeded error/stack processing if only propagating trace-context. | ||
| if (this._conf.contextPropagationOnly) { | ||
| if (cb) { | ||
|
|
@@ -534,8 +537,7 @@ Agent.prototype.captureError = function (err, opts, cb) { | |
| } else { | ||
| inflightEvents.delete(id) | ||
| if (cb) { | ||
| // TODO: Swallow this error just as it's done in agent.flush()? | ||
| cb(new Error('cannot capture error before agent is started'), id) | ||
| cb(new Error('cannot send error: missing transport'), id) | ||
| } | ||
| } | ||
| }) | ||
|
|
@@ -556,9 +558,9 @@ Agent.prototype.handleUncaughtExceptions = function (cb) { | |
| this._uncaughtExceptionListener = function (err) { | ||
| agent.logger.debug({ err }, 'Elastic APM caught unhandled exception') | ||
| // The stack trace of uncaught exceptions are normally written to STDERR. | ||
| // The `uncaughtException` listener inhibits this behavor, and it's | ||
| // The `uncaughtException` listener inhibits this behavior, and it's | ||
| // therefore necessary to manually do this to not break expectations. | ||
| if (agent._conf.logUncaughtExceptions === true) { | ||
| if (agent._conf && agent._conf.logUncaughtExceptions === true) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. REVIEW NOTE: This is a guard against calling agent.handleUncaughtExceptions() and then capturing an uncaught exception before agent.start() |
||
| console.error(err) | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to getting the agent a logger immediately upon instantiation. However, without this call to this._conf the agent will no longer have a configuration object between its initial module loading and the agent starting.
Are we OK breaking this not-explicit-but-a-bit-implicit API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am. ;)
My first justification is that this is an internal/undocumented property. We cannot, IMHO, in general have internal code changes be disallowed by possible/unknown external usage.
The main justification is that
agent._confbetween Agent creation andagent.start(...)is incomplete -- a stronger word is that this _conf is wrong and misleading. External code should not be making behaviour decisions based on a config value that will be different afteragent.start(...). It could lead to a more subtle bug. (The obvious exception here is the logLevel, which we need for internal logging beforeagent.start(). If a good use case for another exception comes up, I think having the specific use case would much better inform how we support it.)The last justification is that I would be surprised if there is any meaningful usage of the Agent instance in customer code before
agent.start(...)is called.