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言語を試し終えた。次回記事で、総評や今後試してみたいことなどを書き連ねてみることにしよう。