Skip to content
This repository was archived by the owner on Aug 4, 2023. It is now read-only.

Commit aee6efb

Browse files
committed
feat: generate metadata object automatically (#6)
1 parent 98e058d commit aee6efb

File tree

6 files changed

+378
-159
lines changed

6 files changed

+378
-159
lines changed

README.md

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,10 @@ npm install elastic-apm-http-client --save
2727
const Client = require('elastic-apm-http-client')
2828

2929
const client = new Client({
30-
userAgent: 'My Custom Elastic APM Agent',
31-
meta: function () {
32-
return {
33-
// meta data object sent as the first ndjson object in all HTTP
34-
// requests to the APM Server
35-
}
36-
}
30+
serviceName: 'My App',
31+
agentName: 'my-nodejs-agent',
32+
agentVersion: require('./package.json').version,
33+
userAgent: 'My Custom Elastic APM Agent'
3734
})
3835

3936
const span = {
@@ -58,14 +55,22 @@ Arguments:
5855

5956
- `options` - An object containing config options (see below)
6057

58+
Data sent to the APM Server as part of the metadata package:
59+
60+
- `agentName` - (required) The APM agent name
61+
- `agentVersion` - (required) The APM agent version
62+
- `serviceName` - (required) The name of the service being instrumented
63+
- `serviceVersion` - The version of the service being instrumented
64+
- `frameworkName` - If the service being instrumented is running a
65+
specific framework, use this config option to log its name
66+
- `frameworkVersion` - If the service being instrumented is running a
67+
specific framework, use this config option to log its version
68+
- `hostname` - Custom hostname (default: OS hostname)
69+
6170
HTTP client configuration:
6271

6372
- `userAgent` - (required) The HTTP user agent that your module should
6473
identify it self as
65-
- `meta` - (required) A function which will be called every time the a
66-
new HTTP request is being made to the APM Server. It's expected that
67-
you return a metadata object. This object will be sent as the first
68-
ndjson object to the API
6974
- `secretToken` - The Elastic APM intake API secret token
7075
- `serverUrl` - The APM Server URL (default: `http://localhost:8200`)
7176
- `headers` - An object containing extra HTTP headers that should be
@@ -98,6 +103,12 @@ Streaming configuration:
98103
to the APM Server can be ongoing before it's ended (default: `10000`
99104
ms)
100105

106+
Data sanitizing configuration:
107+
108+
- `truncateStringsAt` - Maximum size in bytes for strings stored as
109+
Elasticsearch keywords. Strings larger than this will be trucated
110+
(default: `1024` bytes)
111+
101112
### Event: `close`
102113

103114
The `close` event is emitted when the client and any of its underlying

index.js

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict'
22

33
const util = require('util')
4+
const os = require('os')
45
const parseUrl = require('url').parse
56
const zlib = require('zlib')
67
const Writable = require('readable-stream').Writable
@@ -10,11 +11,20 @@ const eos = require('end-of-stream')
1011
const safeStringify = require('fast-safe-stringify')
1112
const streamToBuffer = require('fast-stream-to-buffer')
1213
const StreamChopper = require('stream-chopper')
14+
const truncate = require('unicode-byte-truncate')
1315
const pkg = require('./package')
1416

17+
module.exports = Client
18+
1519
const flush = Symbol('flush')
1620

17-
module.exports = Client
21+
const hostname = os.hostname()
22+
const requiredOpts = [
23+
'agentName',
24+
'agentVersion',
25+
'serviceName',
26+
'userAgent'
27+
]
1828

1929
// All sockets on the agent are unreffed when they are created. This means that
2030
// when those are the only handles left, the `beforeExit` event will be
@@ -155,9 +165,8 @@ Client.prototype.destroy = function (err) {
155165
}
156166

157167
function onStream (opts, client, onerror) {
158-
const meta = opts.meta
159168
const serverTimeout = opts.serverTimeout
160-
opts = getRequestOptions(opts, client._agent)
169+
const requestOpts = getRequestOptions(opts, client._agent)
161170

162171
return function (stream, next) {
163172
const onerrorproxy = (err) => {
@@ -170,7 +179,7 @@ function onStream (opts, client, onerror) {
170179

171180
client._active = true
172181

173-
const req = client._transport.request(opts, onResult(onerror))
182+
const req = client._transport.request(requestOpts, onResult(onerror))
174183
const compressor = zlib.createGzip()
175184

176185
// Mointor streams for errors so that we can make sure to destory the
@@ -219,7 +228,7 @@ function onStream (opts, client, onerror) {
219228
})
220229

221230
// All requests to the APM Server must start with a metadata object
222-
stream.write(safeStringify({metadata: meta()}) + '\n')
231+
stream.write(safeStringify({metadata: metadata(opts)}) + '\n')
223232
}
224233
}
225234

@@ -242,8 +251,8 @@ function onResult (onerror) {
242251
}
243252

244253
function normalizeOptions (opts) {
245-
if (!opts.userAgent) throw new Error('Missing required option: userAgent')
246-
if (!opts.meta) throw new Error('Missing required option: meta')
254+
const missing = requiredOpts.filter(name => !opts[name])
255+
if (missing.length > 0) throw new Error('Missing required option(s): ' + missing.join(', '))
247256

248257
const normalized = Object.assign({}, opts, {objectMode: true})
249258

@@ -252,6 +261,8 @@ function normalizeOptions (opts) {
252261
if (!normalized.time && normalized.time !== 0) normalized.time = 10000
253262
if (!normalized.serverTimeout && normalized.serverTimeout !== 0) normalized.serverTimeout = 15000
254263
if (!normalized.serverUrl) normalized.serverUrl = 'http://localhost:8200'
264+
if (!normalized.hostname) normalized.hostname = hostname
265+
if (!normalized.truncateStringsAt) normalized.truncateStringsAt = 1024
255266
normalized.keepAlive = normalized.keepAlive !== false
256267

257268
// process
@@ -282,3 +293,44 @@ function getHeaders (opts) {
282293
headers['User-Agent'] = opts.userAgent + ' ' + pkg.name + '/' + pkg.version
283294
return Object.assign(headers, opts.headers)
284295
}
296+
297+
function metadata (opts) {
298+
var payload = {
299+
service: {
300+
name: opts.serviceName,
301+
runtime: {
302+
name: process.release.name,
303+
version: process.version
304+
},
305+
language: {
306+
name: 'javascript'
307+
},
308+
agent: {
309+
name: opts.agentName,
310+
version: opts.agentVersion
311+
}
312+
},
313+
process: {
314+
pid: process.pid,
315+
ppid: process.ppid,
316+
title: truncate(String(process.title), opts.truncateStringsAt),
317+
argv: process.argv
318+
},
319+
system: {
320+
hostname: opts.hostname,
321+
architecture: process.arch,
322+
platform: process.platform
323+
}
324+
}
325+
326+
if (opts.serviceVersion) payload.service.version = opts.serviceVersion
327+
328+
if (opts.frameworkName || opts.frameworkVersion) {
329+
payload.service.framework = {
330+
name: opts.frameworkName,
331+
version: opts.frameworkVersion
332+
}
333+
}
334+
335+
return payload
336+
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"ndjson": "^1.5.0",
2323
"pump": "^3.0.0",
2424
"readable-stream": "^2.3.6",
25-
"stream-chopper": "^1.1.1"
25+
"stream-chopper": "^1.1.1",
26+
"unicode-byte-truncate": "^1.0.0"
2627
},
2728
"devDependencies": {
2829
"codecov": "^3.0.4",

0 commit comments

Comments
 (0)