Skip to content

Commit d09d6da

Browse files
david-lunatrentm
authored andcommitted
chore: move 'elastic-apm-http-client' package code into this repo (elastic#3507)
This moves the apm-nodejs-http-client.git code into "{lib,test}/apm-client/http-apm-client/..." in this repo. This will help maintenance of multiple active branches of the APM agent. The only changes are to rename the export to `HttpApmClient` and adjust require paths. Some functions were moved from lib/apm-client/http-apm-client.js to lib/apm-client/apm-client.js to clarify. Closes: elastic#3506 Co-authored-by: Trent Mick <[email protected]>
1 parent a47b210 commit d09d6da

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+8045
-264
lines changed

CHANGELOG.asciidoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ Notes:
5353
[float]
5454
===== Chores
5555
56+
* Inline the `elastic-apm-http-client` package code into this repo.
57+
({issues}3506[#3506])
58+
5659
5760
[[release-notes-3.48.0]]
5861
==== 3.48.0 - 2023/07/07

NOTICE.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,26 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
4141
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4242
```
4343

44+
## container-info
45+
46+
- **path:** [lib/container-info.js](lib/container-info.js)
47+
- **author:** Stephen Belanger
48+
- **project url:** https://github.com/Qard/container-info
49+
- **original file:** https://github.com/Qard/container-info/blob/master/index.js
50+
- **license:** MIT License (MIT), http://opensource.org/licenses/MIT
51+
52+
```
53+
### Copyright (c) 2018 Stephen Belanger
54+
55+
#### Licensed under MIT License
56+
57+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
58+
59+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
60+
61+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
62+
```
63+
4464
## shimmer
4565

4666
- **path:** [lib/instrumentation/shimmer.js](lib/instrumentation/shimmer.js)

lib/apm-client/apm-client.js

Lines changed: 174 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@
66

77
'use strict'
88

9-
const ElasticAPMHttpClient = require('elastic-apm-http-client')
10-
11-
const { CENTRAL_CONFIG_OPTS } = require('../config/schema')
9+
const fs = require('fs')
10+
const version = require('../../package').version
11+
const { CENTRAL_CONFIG_OPTS, INTAKE_STRING_MAX_SIZE } = require('../config/schema')
1212
const { normalize } = require('../config/config')
13+
const { CloudMetadata } = require('../cloud-metadata')
14+
const { isLambdaExecutionEnvironment } = require('../lambda')
1315
const logging = require('../logging')
1416

17+
const { HttpApmClient } = require('./http-apm-client')
1518
const { NoopApmClient } = require('./noop-apm-client')
16-
const { getHttpClientConfig } = require('./http-apm-client')
19+
const { isAzureFunctionsEnvironment, getAzureFunctionsExtraMetadata } = require('../instrumentation/azure-functions')
1720

1821
/**
1922
* Returns an APM client suited for the configuration provided
@@ -28,7 +31,7 @@ function createApmClient (config, agent) {
2831
return config.transport(config, agent)
2932
}
3033

31-
const client = new ElasticAPMHttpClient(getHttpClientConfig(config, agent))
34+
const client = new HttpApmClient(getHttpClientConfig(config, agent))
3235

3336
client.on('config', remoteConf => {
3437
agent.logger.debug({ remoteConf }, 'central config received')
@@ -104,6 +107,171 @@ function createApmClient (config, agent) {
104107
return client
105108
}
106109

110+
/**
111+
* Returns a HTTP client configuration based on agent configuration options
112+
*
113+
* @param {Object} conf The agent configuration object
114+
* @param {Object} agent
115+
* @returns {Object}
116+
*/
117+
function getHttpClientConfig (conf, agent) {
118+
let clientLogger = null
119+
if (!logging.isLoggerCustom(agent.logger)) {
120+
// https://www.elastic.co/guide/en/ecs/current/ecs-event.html#field-event-module
121+
clientLogger = agent.logger.child({ 'event.module': 'apmclient' })
122+
}
123+
const isLambda = isLambdaExecutionEnvironment()
124+
125+
const clientConfig = {
126+
agentName: 'nodejs',
127+
agentVersion: version,
128+
agentActivationMethod: agent._agentActivationMethod,
129+
serviceName: conf.serviceName,
130+
serviceVersion: conf.serviceVersion,
131+
frameworkName: conf.frameworkName,
132+
frameworkVersion: conf.frameworkVersion,
133+
globalLabels: maybePairsToObject(conf.globalLabels),
134+
configuredHostname: conf.hostname,
135+
environment: conf.environment,
136+
137+
// Sanitize conf
138+
truncateKeywordsAt: INTAKE_STRING_MAX_SIZE,
139+
truncateLongFieldsAt: conf.longFieldMaxLength,
140+
// truncateErrorMessagesAt: see below
141+
142+
// HTTP conf
143+
secretToken: conf.secretToken,
144+
apiKey: conf.apiKey,
145+
userAgent: userAgentFromConf(conf),
146+
serverUrl: conf.serverUrl,
147+
serverCaCert: loadServerCaCertFile(conf.serverCaCertFile),
148+
rejectUnauthorized: conf.verifyServerCert,
149+
serverTimeout: conf.serverTimeout * 1000,
150+
151+
// APM Agent Configuration via Kibana:
152+
centralConfig: conf.centralConfig,
153+
154+
// Streaming conf
155+
size: conf.apiRequestSize,
156+
time: conf.apiRequestTime * 1000,
157+
maxQueueSize: conf.maxQueueSize,
158+
159+
// Debugging/testing options
160+
logger: clientLogger,
161+
payloadLogFile: conf.payloadLogFile,
162+
apmServerVersion: conf.apmServerVersion,
163+
164+
// Container conf
165+
containerId: conf.containerId,
166+
kubernetesNodeName: conf.kubernetesNodeName,
167+
kubernetesNamespace: conf.kubernetesNamespace,
168+
kubernetesPodName: conf.kubernetesPodName,
169+
kubernetesPodUID: conf.kubernetesPodUID
170+
}
171+
172+
// `service_node_name` is ignored in Lambda and Azure Functions envs.
173+
if (conf.serviceNodeName) {
174+
if (isLambda) {
175+
agent.logger.warn({ serviceNodeName: conf.serviceNodeName }, 'ignoring "serviceNodeName" config setting in Lambda environment')
176+
} else if (isAzureFunctionsEnvironment) {
177+
agent.logger.warn({ serviceNodeName: conf.serviceNodeName }, 'ignoring "serviceNodeName" config setting in Azure Functions environment')
178+
} else {
179+
clientConfig.serviceNodeName = conf.serviceNodeName
180+
}
181+
}
182+
183+
// Extra metadata handling.
184+
if (isLambda) {
185+
// Tell the Client to wait for a subsequent `.setExtraMetadata()` call
186+
// before allowing intake requests. This will be called by `apm.lambda()`
187+
// on first Lambda function invocation.
188+
clientConfig.expectExtraMetadata = true
189+
} else if (isAzureFunctionsEnvironment) {
190+
clientConfig.extraMetadata = getAzureFunctionsExtraMetadata()
191+
} else if (conf.cloudProvider !== 'none') {
192+
clientConfig.cloudMetadataFetcher = new CloudMetadata(conf.cloudProvider, conf.logger, conf.serviceName)
193+
}
194+
195+
if (conf.errorMessageMaxLength !== undefined) {
196+
// As of v10 of the http client, truncation of error messages will default
197+
// to `truncateLongFieldsAt` if `truncateErrorMessagesAt` is not specified.
198+
clientConfig.truncateErrorMessagesAt = conf.errorMessageMaxLength
199+
}
200+
201+
return clientConfig
202+
}
203+
204+
// Return the User-Agent string the agent will use for its comms to APM Server.
205+
//
206+
// Per https://github.com/elastic/apm/blob/main/specs/agents/transport.md#user-agent
207+
// the pattern is roughly this:
208+
// $repoName/$version ($serviceName $serviceVersion)
209+
//
210+
// The format of User-Agent is governed by https://datatracker.ietf.org/doc/html/rfc7231.
211+
// User-Agent = product *( RWS ( product / comment ) )
212+
// We do not expect `$repoName` and `$version` to have surprise/invalid values.
213+
// From `validateServiceName` above, we know that `$serviceName` is null or a
214+
// string limited to `/^[a-zA-Z0-9 _-]+$/`. However, `$serviceVersion` is
215+
// provided by the user and could have invalid characters.
216+
//
217+
// `comment` is defined by
218+
// https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6 as:
219+
// comment = "(" *( ctext / quoted-pair / comment ) ")"
220+
// obs-text = %x80-FF
221+
// ctext = HTAB / SP / %x21-27 / %x2A-5B / %x5D-7E / obs-text
222+
// quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
223+
//
224+
// `commentBadChar` below *approximates* these rules, and is used to replace
225+
// invalid characters with '_' in the generated User-Agent string. This
226+
// replacement isn't part of the APM spec.
227+
function userAgentFromConf (conf) {
228+
let userAgent = `apm-agent-nodejs/${version}`
229+
230+
// This regex *approximately* matches the allowed syntax for a "comment".
231+
// It does not handle "quoted-pair" or a "comment" in a comment.
232+
const commentBadChar = /[^\t \x21-\x27\x2a-\x5b\x5d-\x7e\x80-\xff]/g
233+
const commentParts = []
234+
if (conf.serviceName) {
235+
commentParts.push(conf.serviceName)
236+
}
237+
if (conf.serviceVersion) {
238+
commentParts.push(conf.serviceVersion.replace(commentBadChar, '_'))
239+
}
240+
if (commentParts.length > 0) {
241+
userAgent += ` (${commentParts.join(' ')})`
242+
}
243+
244+
return userAgent
245+
}
246+
247+
/**
248+
* Reads te server CA cert file and returns a buffer with its contents
249+
* @param {string | undefined} serverCaCertFile
250+
* @param {any} logger
251+
* @returns {Buffer}
252+
*/
253+
function loadServerCaCertFile (serverCaCertFile, logger) {
254+
if (serverCaCertFile) {
255+
try {
256+
return fs.readFileSync(serverCaCertFile)
257+
} catch (err) {
258+
logger.error('Elastic APM initialization error: Can\'t read server CA cert file %s (%s)', serverCaCertFile, err.message)
259+
}
260+
}
261+
}
262+
263+
function maybePairsToObject (pairs) {
264+
return pairs ? pairsToObject(pairs) : undefined
265+
}
266+
267+
function pairsToObject (pairs) {
268+
return pairs.reduce((object, [key, value]) => {
269+
object[key] = value
270+
return object
271+
}, {})
272+
}
273+
107274
module.exports = {
108-
createApmClient
275+
createApmClient,
276+
userAgentFromConf
109277
}

0 commit comments

Comments
 (0)