gcpug / nouhau

Google Cloud Platformのノウハウを共有するRepository
https://gcpug.jp
MIT License
653 stars 23 forks source link

Stackdriver LoggingでJsonPayloadに大きな数値を出力すると誤差が出る #69

Open sinmetal opened 5 years ago

sinmetal commented 5 years ago

WHAT

Stackdriver LoggingのJsonPayloadに大きな数値を出力すると、誤差が出る。 JavaScriptで誤差が出る時と同じ値になるので、どこかでNode.jsとかが処理をしている?

gcloud logging write projects/gcpugjp/logs/bignum --log-http --payload-type=json '{"num": 36028797018963966, "text": "36028797018963966"}'
=======================
==== request start ====
uri: https://logging.googleapis.com/v2/entries:write?alt=json
method: POST
== headers start ==
Authorization: --- Token Redacted ---
accept: application/json
accept-encoding: gzip, deflate
content-length: 201
content-type: application/json
user-agent: google-cloud-sdk x_Tw5K8nnjoRAqULM9PFAC2b gcloud/217.0.0 command/gcloud.logging.write invocation-id/c4ec4b9252264851b33d9ac029133c8d environment/None environment-version/None interactive/True from-script/False python/2.7.14 (Macintosh; Intel Mac OS X 16.7.0)
== headers end ==
== body start ==
{"entries": [{"jsonPayload": {"num": 36028797018963966, "text": "36028797018963966"}, "logName": "projects/gcpugjp/logs/bignum", "resource": {"type": "global"}, "severity": "DEFAULT"}]}
== body end ==
==== request end ====
---- response start ----
-- headers start --
-content-encoding: gzip
alt-svc: quic=":443"; ma=2592000; v="44,43,39,35"
cache-control: private
content-length: 3
content-type: application/json; charset=UTF-8
date: Tue, 02 Oct 2018 10:25:37 GMT
server: ESF
status: 200
transfer-encoding: chunked
vary: Origin, X-Origin, Referer
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
-- headers end --
-- body start --
{}

-- body end --
total round trip time (request+response): 0.182 secs
---- response end ----
----------------------
Created log entry.

投げたPayloadはnumもtextも 36028797018963966 だが、取得するとnumは 36028797018963968 になっている。

gcloud logging read projects/gcpugjp/logs/bignum
---
insertId: iljpr3f1gkkso
jsonPayload:
  num: 36028797018963968
  text: '36028797018963966'
logName: projects/gcpugjp/logs/bignum
receiveTimestamp: '2018-10-02T10:25:37.510646519Z'
resource:
  labels:
    project_id: gcpugjp
  type: global
timestamp: '2018-10-02T10:25:37.510646519Z'
---

どうまるめられるかのメモ

36028797018963970→36028797018963970:  0
36028797018963969→36028797018963970: +1
36028797018963968→36028797018963970: +2
36028797018963967→36028797018963970: +3
36028797018963966→36028797018963970: +2
36028797018963965→36028797018963964: -1
36028797018963964→36028797018963964:  0
36028797018963963→36028797018963964: +1
36028797018963962→36028797018963960: -2
36028797018963961→36028797018963960: -1
36028797018963960→36028797018963960:  0
36028797018963959→36028797018963960: +1
36028797018963958→36028797018963960: +2
36028797018963957→36028797018963956: -1
36028797018963956→36028797018963956:  0
36028797018963955→36028797018963956: +1
36028797018963954→36028797018963950: -4
36028797018963953→36028797018963950: -3
36028797018963952→36028797018963950: -2
36028797018963951→36028797018963950: -1
36028797018963950→36028797018963950:  0
 36028797018963859→36028797018963858: -1
26028797018963969→26028797018963969:  0

WHY

GKEで標準出力に1lineでJsonを出力すれば https://cloud.google.com/logging/docs/structured-logging として扱ってくれるが、大きな数値はまるめられちゃうので、stringに変換するなどの工夫が必要そう。

sinmetal commented 5 years ago

GKEがどうやってログ送ってるかは https://speakerdeck.com/ytakky2014/gkedeha-stackdriver-loggingni-douyatuteroguwosong-tuteiruka が詳しい。 でも、この問題は gcloud logging write でも発生するので、GKEやfluentdはあまり関係ないようだ。

sinmetal commented 5 years ago

GoのLogging Libraryだけですべてが解決するのかは分からないが、修正が入ってるようだ https://github.com/googleapis/google-cloud-go/issues/1150

apstndb commented 5 years ago

文字列として送るのであれば丸められる問題は解決するだろうけど、すでに行われているであろう数値としての比較がぶっ壊れるから今は修正できないというのが結論っぽい https://github.com/googleapis/google-cloud-go/issues/1150#issuecomment-518453164

sinmetal commented 5 years ago

ワカル

sinmetal commented 5 years ago

僕はログから直で引っ張って比較とかをしたことないけれど、Stackdriver側でstringに変換するより、アプリケーション側でstringにして吐き出した方が無難かなぁという気持ち

apstndb commented 5 years ago

Payload interface{} に map じゃなくて struct を渡すのであれば、 int64 なフィールドの struct tag に string をつけておけば良いのではないかという気がする。 https://godoc.org/encoding/json#Marshal

The "string" option signals that a field is stored as JSON inside a JSON-encoded string. It applies only to fields of string, floating point, integer, or boolean types.