zero-code計装シリーズ、いよいよ対応言語最後のJavaScriptとなった。
kmuto.hatenablog.com kmuto.hatenablog.com kmuto.hatenablog.com kmuto.hatenablog.com kmuto.hatenablog.com
JavaScriptというかNode.jsへの計装。実行時に計装ライブラリを注入してテレメトリーを取り出す仕組みになっている。
いつもの雑なWebサーバーを立てる。Expressフレームワークを利用することにした。
npm init --yes npm install express
index.jsを書く。
const express = require("express");
const app = express();
app.get("/", (req, res) => {
res.send("Hello, World\n");
});
app.get("/error", (req, res) => {
throw new Error("Internal Server Error");
});
app.get("/500", (req, res) => {
res.status(500).send("Internal Server Error");
});
app.listen(5000, () => {
console.log("Server running at http://localhost:5000/");
});
node index.jsを実行し、curlで結果が返ってくることを確認。
$ curl http://localhost:5000 Hello, World $ curl http://localhost:5000/error <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Error</title> </head> <body> <pre>Error: Internal Server Error<br> at /home/kmuto/nodejs-zerocode/index.js:9:9<br> at Layer.handle [as handle_request] (/home/kmuto/nodejs-zerocode/node_modules/express/lib/router/layer.js:95:5)<br> at next (/home/kmuto/nodejs-zerocode/node_modules/express/lib/router/route.js:149:13)<br> at Route.dispatch (/home/kmuto/nodejs-zerocode/node_modules/express/lib/router/route.js:119:3)<br> at Layer.handle [as handle_request] (/home/kmuto/nodejs-zerocode/node_modules/express/lib/router/layer.js:95:5)<br> at /home/kmuto/nodejs-zerocode/node_modules/express/lib/router/index.js:284:15<br> at Function.process_params (/home/kmuto/nodejs-zerocode/node_modules/express/lib/router/index.js:346:12)<br> at next (/home/kmuto/nodejs-zerocode/node_modules/express/lib/router/index.js:280:10)<br> at expressInit (/home/kmuto/nodejs-zerocode/node_modules/express/lib/middleware/init.js:40:5)<br> at Layer.handle [as handle_request] (/home/kmuto/nodejs-zerocode/node_modules/express/lib/router/layer.js:95:5)</pre> </body> </html> $ curl http://localhost:5000/500 Internal Server Error
ドキュメントに従い、計装パッケージをインストールする。
npm install --save @opentelemetry/api npm install --save @opentelemetry/auto-instrumentations-node
OpenTelemetry Collectorをいつものとおり実行しておく。
receivers:
otlp:
protocols:
http:
exporters:
debug:
verbosity: detailed
service:
pipelines:
metrics:
receivers: [otlp]
exporters: [debug]
logs:
receivers: [otlp]
exporters: [debug]
traces:
receivers: [otlp]
exporters: [debug]
では、zero-code計装付きで実行。--require @opentelemetry/auto-instrumentations-node/registerで注入させている。
OTEL_SERVICE_NAME=nodejs-zerocode node --require @opentelemetry/auto-instrumentations-node/register index.js
curlしながらトレースの様子を見てみよう。
2025-02-01T21:32:38.791+0900 info Traces {"kind": "exporter", "data_type": "traces", "name": "debug", "resource spans": 1, "spans": 4}
2025-02-01T21:32:38.791+0900 info ResourceSpans #0
Resource SchemaURL:
Resource attributes:
-> service.name: Str(nodejs-zerocode)
-> telemetry.sdk.language: Str(nodejs)
-> telemetry.sdk.name: Str(opentelemetry)
-> telemetry.sdk.version: Str(1.30.1)
-> host.name: Str(...)
-> host.arch: Str(amd64)
-> os.type: Str(linux)
-> os.version: Str(6.1.0-30-amd64)
-> service.instance.id: Str(76577b3b-41ff-4a0a-b383-499c13cb9a7e)
-> process.pid: Int(1250110)
-> process.executable.name: Str(node)
-> process.executable.path: Str(/usr/bin/node)
-> process.command_args: Slice(["/usr/bin/node","--require","@opentelemetry/auto-instrumentations-node/register","/home/kmuto/nodejs-zerocode/index.js"])
-> process.runtime.version: Str(20.18.1)
-> process.runtime.name: Str(nodejs)
-> process.runtime.description: Str(Node.js)
-> process.command: Str(/home/kmuto/nodejs-zerocode/index.js)
-> process.owner: Str(kmuto)
-> host.id: Str(86fdb17091114232ba104aea4bc5250d)
ScopeSpans #0
ScopeSpans SchemaURL:
InstrumentationScope @opentelemetry/instrumentation-net 0.43.0
Span #0
Trace ID : caa5bd201ff9f307aebce2f7e2ec2aa7
Parent ID : 0c738bec56d427a2
ID : 1e3edea415bc59d2
Name : tcp.connect
Kind : Internal
Start time : 2025-02-01 12:32:33.775 +0000 UTC
End time : 2025-02-01 12:32:33.78085516 +0000 UTC
Status code : Error
Status message : getaddrinfo ENOTFOUND metadata.google.internal.
Attributes:
-> net.transport: Str(ip_tcp)
-> net.peer.name: Str(metadata.google.internal.)
-> net.peer.port: Int(80)
Span #1
Trace ID : 7c0859294fc05427999ea55420b5c5ed
Parent ID : c8f98b03201b8700
ID : 71ece3918be021b5
Name : tcp.connect
Kind : Internal
Start time : 2025-02-01 12:32:33.771 +0000 UTC
End time : 2025-02-01 12:32:36.777999004 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> net.transport: Str(ip_tcp)
-> net.peer.name: Str(169.254.169.254)
-> net.peer.port: Int(80)
ScopeSpans #1
ScopeSpans SchemaURL:
InstrumentationScope @opentelemetry/instrumentation-http 0.57.1
Span #0
Trace ID : caa5bd201ff9f307aebce2f7e2ec2aa7
Parent ID :
ID : 0c738bec56d427a2
Name : GET
Kind : Client
Start time : 2025-02-01 12:32:33.772 +0000 UTC
End time : 2025-02-01 12:32:33.781158297 +0000 UTC
Status code : Error
Status message : getaddrinfo ENOTFOUND metadata.google.internal.
Attributes:
-> http.url: Str(http://metadata.google.internal./computeMetadata/v1/instance)
-> http.method: Str(GET)
-> http.target: Str(/computeMetadata/v1/instance)
-> net.peer.name: Str(metadata.google.internal.)
-> http.host: Str(metadata.google.internal.:80)
-> http.error_name: Str(Error)
-> http.error_message: Str(getaddrinfo ENOTFOUND metadata.google.internal.)
Events:
SpanEvent #0
-> Name: exception
-> Timestamp: 2025-02-01 12:32:33.781121043 +0000 UTC
-> DroppedAttributesCount: 0
-> Attributes::
-> exception.type: Str(ENOTFOUND)
-> exception.message: Str(getaddrinfo ENOTFOUND metadata.google.internal.)
-> exception.stacktrace: Str(Error: getaddrinfo ENOTFOUND metadata.google.internal.
at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:120:26)
at GetAddrInfoReqWrap.callbackTrampoline (node:internal/async_hooks:130:17))
Span #1
Trace ID : 7c0859294fc05427999ea55420b5c5ed
Parent ID :
ID : c8f98b03201b8700
Name : GET
Kind : Client
Start time : 2025-02-01 12:32:33.769 +0000 UTC
End time : 2025-02-01 12:32:36.777554317 +0000 UTC
Status code : Error
Status message : socket hang up
Attributes:
-> http.url: Str(http://169.254.169.254/computeMetadata/v1/instance)
-> http.method: Str(GET)
-> http.target: Str(/computeMetadata/v1/instance)
-> net.peer.name: Str(169.254.169.254)
-> http.host: Str(169.254.169.254:80)
-> http.error_name: Str(Error)
-> http.error_message: Str(socket hang up)
Events:
SpanEvent #0
-> Name: exception
-> Timestamp: 2025-02-01 12:32:36.77754689 +0000 UTC
-> DroppedAttributesCount: 0
-> Attributes::
-> exception.type: Str(ECONNRESET)
-> exception.message: Str(socket hang up)
-> exception.stacktrace: Str(Error: socket hang up
at Socket.socketCloseListener (node:_http_client:477:27)
at Socket.emit (node:events:530:35)
at TCP.<anonymous> (node:net:343:12)
at TCP.callbackTrampoline (node:internal/async_hooks:130:17))
{"kind": "exporter", "data_type": "traces", "name": "debug"}
2025-02-01T21:33:13.387+0900 info Traces {"kind": "exporter", "data_type": "traces", "name": "debug", "resource spans": 1, "spans": 8}
2025-02-01T21:33:13.387+0900 info ResourceSpans #0
Resource SchemaURL:
Resource attributes:
-> service.name: Str(nodejs-zerocode)
-> telemetry.sdk.language: Str(nodejs)
-> telemetry.sdk.name: Str(opentelemetry)
-> telemetry.sdk.version: Str(1.30.1)
-> host.name: Str(elemental)
-> host.arch: Str(amd64)
-> os.type: Str(linux)
-> os.version: Str(6.1.0-30-amd64)
-> service.instance.id: Str(76577b3b-41ff-4a0a-b383-499c13cb9a7e)
-> process.pid: Int(1250110)
-> process.executable.name: Str(node)
-> process.executable.path: Str(/usr/bin/node)
-> process.command_args: Slice(["/usr/bin/node","--require","@opentelemetry/auto-instrumentations-node/register","/home/kmuto/nodejs-zerocode/index.js"])
-> process.runtime.version: Str(20.18.1)
-> process.runtime.name: Str(nodejs)
-> process.runtime.description: Str(Node.js)
-> process.command: Str(/home/kmuto/nodejs-zerocode/index.js)
-> process.owner: Str(kmuto)
-> host.id: Str(86fdb17091114232ba104aea4bc5250d)
ScopeSpans #0
ScopeSpans SchemaURL:
InstrumentationScope @opentelemetry/instrumentation-express 0.47.0
Span #0
Trace ID : 9572efd867249b59cbb376732a820d0c
Parent ID : 27c4c6585ef4a6d9
ID : b0247ca292ec8610
Name : middleware - query
Kind : Internal
Start time : 2025-02-01 12:33:08.384 +0000 UTC
End time : 2025-02-01 12:33:08.384331017 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> http.route: Str(/)
-> express.name: Str(query)
-> express.type: Str(middleware)
Span #1
Trace ID : 9572efd867249b59cbb376732a820d0c
Parent ID : 27c4c6585ef4a6d9
ID : 33e78a22a5b79809
Name : middleware - expressInit
Kind : Internal
Start time : 2025-02-01 12:33:08.384 +0000 UTC
End time : 2025-02-01 12:33:08.384114406 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> http.route: Str(/)
-> express.name: Str(expressInit)
-> express.type: Str(middleware)
Span #2
Trace ID : 9572efd867249b59cbb376732a820d0c
Parent ID : 27c4c6585ef4a6d9
ID : 7e56c5d076b97d5a
Name : request handler - /
Kind : Internal
Start time : 2025-02-01 12:33:08.384 +0000 UTC
End time : 2025-02-01 12:33:08.384017304 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> http.route: Str(/)
-> express.name: Str(/)
-> express.type: Str(request_handler)
Span #3
Trace ID : 906fcf5a00e00da4750ce5cbc9597179
Parent ID : 7fc2baee42bada97
ID : e2b782bd9eb7ff6a
Name : middleware - query
Kind : Internal
Start time : 2025-02-01 12:33:13.3 +0000 UTC
End time : 2025-02-01 12:33:13.300063332 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> http.route: Str(/)
-> express.name: Str(query)
-> express.type: Str(middleware)
Span #4
Trace ID : 906fcf5a00e00da4750ce5cbc9597179
Parent ID : 7fc2baee42bada97
ID : d6ee11556a6e8a44
Name : middleware - expressInit
Kind : Internal
Start time : 2025-02-01 12:33:13.301 +0000 UTC
End time : 2025-02-01 12:33:13.301048907 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> http.route: Str(/)
-> express.name: Str(expressInit)
-> express.type: Str(middleware)
Span #5
Trace ID : 906fcf5a00e00da4750ce5cbc9597179
Parent ID : 7fc2baee42bada97
ID : b7bb9416e075767b
Name : request handler - /error
Kind : Internal
Start time : 2025-02-01 12:33:13.301 +0000 UTC
End time : 2025-02-01 12:33:13.301092888 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> http.route: Str(/error)
-> express.name: Str(/error)
-> express.type: Str(request_handler)
ScopeSpans #1
ScopeSpans SchemaURL:
InstrumentationScope @opentelemetry/instrumentation-http 0.57.1
Span #0
Trace ID : 9572efd867249b59cbb376732a820d0c
Parent ID :
ID : 27c4c6585ef4a6d9
Name : GET /
Kind : Server
Start time : 2025-02-01 12:33:08.382 +0000 UTC
End time : 2025-02-01 12:33:08.387360158 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> http.url: Str(http://localhost:5000/)
-> http.host: Str(localhost:5000)
-> net.host.name: Str(localhost)
-> http.method: Str(GET)
-> http.scheme: Str(http)
-> http.target: Str(/)
-> http.user_agent: Str(curl/7.88.1)
-> http.flavor: Str(1.1)
-> net.transport: Str(ip_tcp)
-> net.host.ip: Str(::ffff:127.0.0.1)
-> net.host.port: Int(5000)
-> net.peer.ip: Str(::ffff:127.0.0.1)
-> net.peer.port: Int(57458)
-> http.status_code: Int(200)
-> http.status_text: Str(OK)
-> http.route: Str(/)
Span #1
Trace ID : 906fcf5a00e00da4750ce5cbc9597179
Parent ID :
ID : 7fc2baee42bada97
Name : GET /error
Kind : Server
Start time : 2025-02-01 12:33:13.3 +0000 UTC
End time : 2025-02-01 12:33:13.30250585 +0000 UTC
Status code : Error
Status message :
Attributes:
-> http.url: Str(http://localhost:5000/error)
-> http.host: Str(localhost:5000)
-> net.host.name: Str(localhost)
-> http.method: Str(GET)
-> http.scheme: Str(http)
-> http.target: Str(/error)
-> http.user_agent: Str(curl/7.88.1)
-> http.flavor: Str(1.1)
-> net.transport: Str(ip_tcp)
-> net.host.ip: Str(::ffff:127.0.0.1)
-> net.host.port: Int(5000)
-> net.peer.ip: Str(::ffff:127.0.0.1)
-> net.peer.port: Int(34950)
-> http.status_code: Int(500)
-> http.status_text: Str(INTERNAL SERVER ERROR)
-> http.route: Str(/error)
{"kind": "exporter", "data_type": "traces", "name": "debug"}
2025-02-01T21:33:21.147+0900 info Traces {"kind": "exporter", "data_type": "traces", "name": "debug", "resource spans": 1, "spans": 4}
2025-02-01T21:33:21.147+0900 info ResourceSpans #0
Resource SchemaURL:
Resource attributes:
-> service.name: Str(nodejs-zerocode)
-> telemetry.sdk.language: Str(nodejs)
-> telemetry.sdk.name: Str(opentelemetry)
-> telemetry.sdk.version: Str(1.30.1)
-> host.name: Str(elemental)
-> host.arch: Str(amd64)
-> os.type: Str(linux)
-> os.version: Str(6.1.0-30-amd64)
-> service.instance.id: Str(76577b3b-41ff-4a0a-b383-499c13cb9a7e)
-> process.pid: Int(1250110)
-> process.executable.name: Str(node)
-> process.executable.path: Str(/usr/bin/node)
-> process.command_args: Slice(["/usr/bin/node","--require","@opentelemetry/auto-instrumentations-node/register","/home/kmuto/nodejs-zerocode/index.js"])
-> process.runtime.version: Str(20.18.1)
-> process.runtime.name: Str(nodejs)
-> process.runtime.description: Str(Node.js)
-> process.command: Str(/home/kmuto/nodejs-zerocode/index.js)
-> process.owner: Str(kmuto)
-> host.id: Str(86fdb17091114232ba104aea4bc5250d)
ScopeSpans #0
ScopeSpans SchemaURL:
InstrumentationScope @opentelemetry/instrumentation-express 0.47.0
Span #0
Trace ID : daf7b2cca043477e1ca19acc3225cb1d
Parent ID : 79219c5dc5b522b9
ID : 5bb002eafa0cfb27
Name : middleware - query
Kind : Internal
Start time : 2025-02-01 12:33:16.145 +0000 UTC
End time : 2025-02-01 12:33:16.145034154 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> http.route: Str(/)
-> express.name: Str(query)
-> express.type: Str(middleware)
Span #1
Trace ID : daf7b2cca043477e1ca19acc3225cb1d
Parent ID : 79219c5dc5b522b9
ID : 087cbf534f01297a
Name : middleware - expressInit
Kind : Internal
Start time : 2025-02-01 12:33:16.145 +0000 UTC
End time : 2025-02-01 12:33:16.145031246 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> http.route: Str(/)
-> express.name: Str(expressInit)
-> express.type: Str(middleware)
Span #2
Trace ID : daf7b2cca043477e1ca19acc3225cb1d
Parent ID : 79219c5dc5b522b9
ID : b8d033ff29b225bc
Name : request handler - /500
Kind : Internal
Start time : 2025-02-01 12:33:16.145 +0000 UTC
End time : 2025-02-01 12:33:16.145006133 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> http.route: Str(/500)
-> express.name: Str(/500)
-> express.type: Str(request_handler)
ScopeSpans #1
ScopeSpans SchemaURL:
InstrumentationScope @opentelemetry/instrumentation-http 0.57.1
Span #0
Trace ID : daf7b2cca043477e1ca19acc3225cb1d
Parent ID :
ID : 79219c5dc5b522b9
Name : GET /500
Kind : Server
Start time : 2025-02-01 12:33:16.145 +0000 UTC
End time : 2025-02-01 12:33:16.145981532 +0000 UTC
Status code : Error
Status message :
Attributes:
-> http.url: Str(http://localhost:5000/500)
-> http.host: Str(localhost:5000)
-> net.host.name: Str(localhost)
-> http.method: Str(GET)
-> http.scheme: Str(http)
-> http.target: Str(/500)
-> http.user_agent: Str(curl/7.88.1)
-> http.flavor: Str(1.1)
-> net.transport: Str(ip_tcp)
-> net.host.ip: Str(::ffff:127.0.0.1)
-> net.host.port: Int(5000)
-> net.peer.ip: Str(::ffff:127.0.0.1)
-> net.peer.port: Int(34958)
-> http.status_code: Int(500)
-> http.status_text: Str(INTERNAL SERVER ERROR)
-> http.route: Str(/500)
{"kind": "exporter", "data_type": "traces", "name": "debug"}
予想外にドバドバ出てきた。
curlの実行前に、metadata.google.internalにアクセスする接続が出てエラーになっていた。常にDNS解決にもアクセスにも失敗しそうなものだが、なんでこんなリクエストを実行しているんだろう。
本題のほうについては、フレームワーク内の「middleware - query」と「middleware - expressInit」という初期化系スパンが生まれている。属性的にはそんなに面白いものはない感じだった。
ハンドラは「request handler」というごく短いスパンになっている。/errorリクエストで直接内部例外起こしたところでは、特に何も属性は付かなかった。
Mackerelにも送ってみた。

だいぶ大雑把ではあったけれども、これでひととおりzero-code計装として公式に言及されている6言語を試し終えた。次回記事で、総評や今後試してみたいことなどを書き連ねてみることにしよう。