{"id":566,"date":"2025-05-08T01:23:03","date_gmt":"2025-05-07T16:23:03","guid":{"rendered":"https:\/\/tako.nakano.net\/blog\/?p=566"},"modified":"2025-05-08T01:23:03","modified_gmt":"2025-05-07T16:23:03","slug":"structured-logging-in-rails-8","status":"publish","type":"post","link":"https:\/\/tako.nakano.net\/blog\/2025\/05\/structured-logging-in-rails-8\/","title":{"rendered":"Rails 8 \u3067\u306e\u69cb\u9020\u5316\u30ed\u30b0\u5b9f\u88c5\u306e\u8ecc\u8de1"},"content":{"rendered":"<h1>Rails 8 \u3067\u306e\u69cb\u9020\u5316\u30ed\u30b0\u5b9f\u88c5\u306e\u8ecc\u8de1<\/h1>\n<p>English follows Japanese.<\/p>\n<h2>\u6982\u8981<\/h2>\n<ul>\n<li>\u69cb\u9020\u5316\u30ed\u30b0\u3068 Cloud Logging \u306e\u7d44\u307f\u5408\u308f\u305b\u306f\u3001\u4fbf\u5229\n<ul>\n<li>\u30d5\u30a3\u30eb\u30bf\u30ea\u30f3\u30b0<\/li>\n<li>\u4e00\u9023\u306e\u30ed\u30b0\u306e\u30b0\u30eb\u30fc\u30d4\u30f3\u30b0<\/li>\n<\/ul>\n<\/li>\n<li>Cloud Run\n<ul>\n<li>traceparent \u30d8\u30c3\u30c0\u30fc\u304c\u6a19\u6e96\u3067\u4ed8\u4e0e\u3055\u308c\u308b<\/li>\n<li>\uff08\u96a0\u308c\u305f\uff09LB \u306e\u30ed\u30b0\u306b\u3082 Trace ID \u304c\u542b\u307e\u308c\u308b<\/li>\n<li>\u6a19\u6e96\u51fa\u529b\u3067\u5b9f\u8a3c<\/li>\n<\/ul>\n<\/li>\n<li>\u5b9f\u884c\u74b0\u5883\n<ul>\n<li>Rails 8<\/li>\n<li>Ruby 3.4<\/li>\n<li>DB \u306a\u3057<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>\u30b9\u30e2\u30fc\u30eb\u30b9\u30bf\u30fc\u30c8\u3067\u3001\u975e\u6a5f\u80fd\u306e\u9762\u306b\u306f\u3042\u307e\u308a\u6642\u9593\u304c\u304b\u3051\u3089\u308c\u306a\u3044\u5834\u5408\u3067\u3082\u3001\u52d5\u304f\u30b3\u30fc\u30c9\u3092\u542b\u3081\u305f\u5148\u884c\u4f8b\u304c\u3042\u308c\u3070\u3001\u5b9f\u88c5\u306e\u53c2\u8003\u306b\u306a\u308b\u3068\u601d\u3044\u307e\u3057\u305f\u3002<\/p>\n<h2>\u306f\u3058\u3081\u306b<\/h2>\n<p>\u672c\u8a18\u4e8b\u3067\u306f\u3001Rails 8 \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306b\u304a\u3051\u308b\u69cb\u9020\u5316\u30ed\u30b0\u306e\u5b9f\u88c5\u4f8b\u3092\u7d39\u4ecb\u3057\u307e\u3059\u3002\u7279\u306b\u3001\u5206\u6563\u30c8\u30ec\u30fc\u30b7\u30f3\u30b0\u306e\u305f\u3081\u306e Trace ID \u3092\u30ed\u30b0\u306b\u542b\u3081\u308b\u65b9\u6cd5\u306b\u7126\u70b9\u3092\u5f53\u3066\u3001<a href=\"https:\/\/logger.rocketjob.io\/\">SemanticLogger<\/a>(<a href=\"https:\/\/github.com\/reidmorrison\/semantic_logger\">GitHub<\/a>, <a href=\"https:\/\/github.com\/reidmorrison\/rails_semantic_logger\">Rails Semantic Logger<\/a>) \u3068\u9023\u643a\u3057\u305f <a href=\"https:\/\/www.w3.org\/TR\/trace-context\/\">W3C TraceContext<\/a> \u306e\u6d3b\u7528\u65b9\u6cd5\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n<p><a href=\"https:\/\/github.com\/roidrage\/lograge\">Lograge<\/a> \u304c\u826f\u304f\u4f7f\u308f\u308c\u3066\u3044\u308b\u5370\u8c61\u304c\u3042\u308a\u307e\u3059\u304c\u3001JSON \u306b\u3057\u3064\u3064\u3001request_id \u3092\u4ed8\u4e0e\u3059\u308b\u306e\u304c\u9762\u5012\u3068\u3044\u3046\u8a18\u4e8b\u3082\u3044\u304f\u3064\u304b\u898b\u304b\u3051\u307e\u3057\u305f\u3002\u305d\u306e\u70b9\u3001SemanticLogger \u306f\u69cb\u9020\u5316\u30ed\u30b0\u3092\u7c21\u5358\u306b\u5b9f\u73fe\u305d\u3046\u3060\u3063\u305f\u306e\u3067\u3001\u8abf\u67fb\u3082\u517c\u306d\u3066\u5b9f\u88c5\u3057\u3066\u307f\u307e\u3057\u305f\u3002<\/p>\n<h2>\u80cc\u666f<\/h2>\n<p>\u30de\u30a4\u30af\u30ed\u30b5\u30fc\u30d3\u30b9\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u304c\u666e\u53ca\u3059\u308b\u4e2d\u3001\u8907\u6570\u30b5\u30fc\u30d3\u30b9\u9593\u3067\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u306e\u8ffd\u8de1\u304c\u91cd\u8981\u306a\u8ab2\u984c\u3068\u306a\u3063\u3066\u3044\u307e\u3059\u3002W3C TraceContext \u306f\u3001\u30b5\u30fc\u30d3\u30b9\u9593\u3067\u30c8\u30ec\u30fc\u30b9\u60c5\u5831\u3092\u4f1d\u64ad\u3059\u308b\u305f\u3081\u306e\u6a19\u6e96\u898f\u683c\u3067\u3001HTTP <code>traceparent<\/code> \u30d8\u30c3\u30c0\u30fc\u3092\u4f7f\u7528\u3057\u3066 Trace ID \u3092\u53d7\u3051\u6e21\u3057\u307e\u3059\u3002<\/p>\n<p>\u5f53\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3067\u306f\u3001\u3053\u306e Trace ID \u3092 Rails \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30ed\u30b0\u306b\u7d71\u5408\u3057\u3001Google Cloud Logging \u3067\u306e\u4e00\u5143\u7684\u306a\u76e3\u8996\u3092\u53ef\u80fd\u306b\u3059\u308b\u3053\u3068\u3092\u76ee\u6a19\u3068\u3057\u307e\u3057\u305f\u3002<\/p>\n<h2>\u307e\u3068\u3081<\/h2>\n<ol>\n<li>middleware \u3092\u65b0\u898f\u5b9f\u88c5\u3057\u3001\u4f7f\u3063\u3066 Request Header (traceparent) \u304b\u3089 Trace ID \u3092\u62bd\u51fa\n<ul>\n<li><code>HTTP_TRACEPARENT<\/code> \u30d8\u30c3\u30c0\u30fc\u3092\u62bd\u51fa\u3057\u3001<code>env[:traceparent]<\/code> \u306b\u4fdd\u5b58<\/li>\n<li>Trace ID \u3092 <code>env[:trace_id]<\/code> \u306b\u4fdd\u5b58<\/li>\n<li><code>req.request_id<\/code> \u306b Trace ID \u3092\u4e0a\u66f8\u304d<\/li>\n<\/ul>\n<\/li>\n<li><code>config.log_tags<\/code> \u3092\u4f7f\u3063\u3066\u3001\u30ea\u30af\u30a8\u30b9\u30c8 ID \u3068 Trace ID \u3092\u30ed\u30b0\u306b\u30bf\u30b0\u4ed8\u3051\n<ul>\n<li><code>traceparent<\/code> \u3068 <code>trace_id<\/code> \u3092 <code>log_tags<\/code> \u306b\u8ffd\u52a0<\/li>\n<\/ul>\n<\/li>\n<li>\u69cb\u9020\u5316\u30ed\u30b0 (JSON) \u3092\u51fa\u529b\u3059\u308b\u305f\u3081\u306e\u30d5\u30a9\u30fc\u30de\u30c3\u30bf\u3092\u5b9f\u88c5\n<ul>\n<li><code>SemanticLogger::Formatters::Json<\/code> \u3068\u307b\u307c\u540c\u3058\u5b9f\u88c5<\/li>\n<li><code>SemanticLogger::Formatters::Raw<\/code> \u3092\u7d99\u627f<\/li>\n<li><code>traceparent<\/code> \u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u3001\u30c8\u30c3\u30d7\u30ec\u30d9\u30eb\u30d5\u30a3\u30fc\u30eb\u30c9\u3068\u3057\u3066\u51fa\u529b<\/li>\n<li><code>logging.googleapis.com\/trace<\/code> \u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u30c8\u30c3\u30d7\u30ec\u30d9\u30eb\u30d5\u30a3\u30fc\u30eb\u30c9\u3068\u3057\u3001<code>projects\/#{ENV[&quot;PROJECT_ID&quot;]}\/traces\/#{trace_id}<\/code> \u3068\u3059\u308b\u5f62\u3067 Trace ID \u3092\u8a2d\u5b9a<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p>\u4e0a\u8a18\u306b\u3088\u3063\u3066\u3001Cloud Logging \u3067 Trace ID \u306b\u3088\u308b\u30ed\u30b0\u306e\u30b0\u30eb\u30fc\u30d4\u30f3\u30b0\u304c\u53ef\u80fd\u306b\u306a\u308a\u307e\u3059\u3002\u5c02\u7528\u306e\u30ed\u30b0 \u30a8\u30fc\u30b8\u30a7\u30f3\u30c8\u306e\u5c0e\u5165\u304c\u4e0d\u8981\u3067\u3001Cloud Run \u306e\u6a19\u6e96\u51fa\u529b\u306b\u76f4\u63a5\u51fa\u529b\u3059\u308b\u3060\u3051\u3067\u3001Google Cloud Logging \u306b\u69cb\u9020\u5316\u30ed\u30b0\u304c\u9001\u4fe1\u3055\u308c\u307e\u3059\u3002<\/p>\n<hr \/>\n<h2>\uff08\u53c2\u8003\uff09Rails \u306b\u304a\u3051\u308b Request ID \u306e\u4ed5\u7d44\u307f<\/h2>\n<p>\u307e\u305a\u3001Rails \u306e\u6a19\u6e96\u7684\u306a\u30ed\u30b0\u306b\u542b\u307e\u308c\u308b <code>request_id<\/code> \u306e\u4ed5\u7d44\u307f\u3092\u7406\u89e3\u3057\u3066\u304a\u304f\u3053\u3068\u304c\u91cd\u8981\u3067\u3059\u3002bisque \u306b\u3088\u308b<a href=\"https:\/\/zenn.dev\/bisque\/scraps\/e0c58eb6fd07fa\">Rails\u306e\u30ed\u30b0\u306b\u542b\u307e\u308c\u308brequest_id\u306b\u3064\u3044\u3066\u30b3\u30fc\u30c9\u30ea\u30fc\u30c7\u30a3\u30f3\u30b0\u3057\u305f\u30e1\u30e2<\/a>\u306b\u3088\u308c\u3070\u3001request_id \u306f\u4ee5\u4e0b\u306e\u3088\u3046\u306b\u51e6\u7406\u3055\u308c\u307e\u3059:<\/p>\n<ol>\n<li><strong>\u751f\u6210<\/strong>: <code>ActionDispatch::RequestId<\/code> \u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u304c Request Header \u306e <code>X-Request-Id<\/code> \u3092\u53d6\u5f97\uff08\u306a\u3044\u5834\u5408\u306f UUID \u3092\u751f\u6210\uff09<\/li>\n<li><strong>\u4f1d\u64ad<\/strong>: \u30ea\u30af\u30a8\u30b9\u30c8\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e <code>request_id<\/code> \u5c5e\u6027\u3068\u3057\u3066\u4fdd\u5b58\u3055\u308c\u3001Response Header \u306b\u3082 <code>X-Request-Id<\/code> \u3068\u3057\u3066\u8a2d\u5b9a\uff08\u6ce8: Request Header \u306a\u306e\u3067\u306f\u2026\u3068\u601d\u3046\u304c\u3001\u826f\u304f\u5206\u304b\u3063\u3066\u3044\u306a\u3044\uff09<\/li>\n<li><strong>\u30ed\u30b0\u51fa\u529b<\/strong>: <code>Rack::Logger<\/code> \u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u304c <code>ActiveSupport::TaggedLogging<\/code> \u3092\u901a\u3058\u3066\u30ed\u30b0\u306b <code>request_id<\/code> \u3092\u633f\u5165<\/li>\n<li><strong>\u30b9\u30ec\u30c3\u30c9\u7ba1\u7406<\/strong>: <code>ActiveSupport::TaggedLogging<\/code> \u306f <code>Thread.current<\/code> \u3092\u4f7f\u3063\u3066\u30bf\u30b0\u3092\u7ba1\u7406\u3057\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u5168\u4f53\u3067\u53c2\u7167\u53ef\u80fd\u306b<\/li>\n<\/ol>\n<p>\u3053\u306e\u4ed5\u7d44\u307f\u3092\u5fdc\u7528\u3057\u3066\u3001W3C TraceContext \u304b\u3089\u306e Trace ID \u3092\u30ed\u30b0\u306b\u7d71\u5408\u3059\u308b\u5b9f\u88c5\u3092\u884c\u3044\u307e\u3059\u3002<\/p>\n<p><code>X-Request-Id<\/code> \u30d8\u30c3\u30c0\u30fc\u306f\u3001\u30ea\u30af\u30a8\u30b9\u30c8\u306e\u30c8\u30ec\u30fc\u30b9\u3092\u8ffd\u8de1\u3059\u308b\u305f\u3081\u306b\u4f7f\u7528\u3055\u308c\u3066\u3044\u307e\u3057\u305f\uff08\u4eca\u3067\u3082\u4f7f\u7528\u3055\u308c\u3066\u3044\u307e\u3059\uff09\u3002\u5206\u6563\u30c8\u30ec\u30fc\u30b7\u30f3\u30b0\u306e\u89b3\u70b9\u304b\u3089\u306f\u3001Trace ID \u3092\u542b\u3080 <code>traceparent<\/code> \u30d8\u30c3\u30c0\u30fc\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u304c\u63a8\u5968\u3055\u308c\u307e\u3059\u3002<\/p>\n<h2>\u5b9f\u88c5\u306e\u30b9\u30c6\u30c3\u30d7<\/h2>\n<h3>1. \u5fc5\u8981\u306a Gem \u306e\u5c0e\u5165<\/h3>\n<p>\u307e\u305a\u3001\u69cb\u9020\u5316\u30ed\u30b0\u51fa\u529b\u306e\u305f\u3081\u306e Gem \u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3057\u307e\u3057\u305f\u3002<code>Gemfile<\/code> \u306b\u4ee5\u4e0b\u3092\u8ffd\u52a0\u3057\u307e\u3059\u3002JSON \u3067\u51fa\u529b\u3059\u308b\u4ed5\u7d44\u307f\u3082\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002<\/p>\n<pre><code class=\"language-ruby:Gemfile\">gem &quot;rails_semantic_logger&quot;<\/code><\/pre>\n<h3>2. \u30c8\u30ec\u30fc\u30b9\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u5b9f\u88c5<\/h3>\n<p>Request Header \u304b\u3089 Trace ID \u3092\u62bd\u51fa\u3059\u308b\u305f\u3081\u306b\u3001\u5c02\u7528\u306e\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u3092\u5b9f\u88c5\u3057\u307e\u3057\u305f\u3002\u3053\u308c\u306f Rails \u306e <code>ActionDispatch::RequestId<\/code> \u3068\u540c\u69d8\u306e\u30a2\u30d7\u30ed\u30fc\u30c1\u3067\u3059\u3002<\/p>\n<p>GitHub \u30ea\u30dd\u30b8\u30c8\u30ea\u3078\u306e\u30ea\u30f3\u30af\u3082\u4ed8\u3057\u3066\u304a\u304d\u307e\u3059\u3002<a href=\"https:\/\/github.com\/takotakot\/rails8-google-cloud-example\/blob\/a4e0f4c95dfd0dfcb6e81b64ca1ce06ce43d5791\/structured-logging\/lib\/trace_middleware.rb\">trace_middleware.rb<\/a><\/p>\n<pre><code class=\"language-ruby:lib\/trace_middleware.rb\"># lib\/trace_middleware.rb\nclass TraceMiddleware\n  def initialize(app)\n    @app = app\n  end\n\n  def call(env)\n    # HTTP_TRACEPARENT \u30d8\u30c3\u30c0\u30fc\u3092\u62bd\u51fa\n    trace_parent = extract_trace_parent(env)\n    if trace_parent\n      env[:traceparent] = trace_parent\n    end\n\n    # \u30d8\u30c3\u30c0\u30fc\u304b\u3089 trace_id \u3092\u62bd\u51fa\u3001\n    # \u306a\u3044\u5834\u5408\u306f SecureRandom.uuid \u3092\u4f7f\u7528\n    env[:trace_id] = make_trace_id(trace_parent)\n\n    # Overwrite the request_id with the trace_id\n    req = ActionDispatch::Request.new env\n    req.request_id = insert_hyphens(env[:trace_id])\n\n    @app.call(env)\n  end\n\n  # ...\nend<\/code><\/pre>\n<p>\u3053\u306e\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306f\u3001W3C TraceContext \u306b\u6e96\u62e0\u3057\u305f <code>traceparent<\/code> \u30d8\u30c3\u30c0\u30fc\uff08<code>HTTP_TRACEPARENT<\/code> \u306b\u4fdd\u5b58\u3055\u308c\u308b\uff09\u304b\u3089 Trace ID \u3092\u62bd\u51fa\u3057\u3001\u30ea\u30af\u30a8\u30b9\u30c8\u74b0\u5883\u5909\u6570\u306b\u4fdd\u5b58\u3057\u307e\u3059\u3002\u30d8\u30c3\u30c0\u30fc\u304c\u5b58\u5728\u3057\u306a\u3044\u5834\u5408\u306f\u3001UUID \u5f62\u5f0f\u306e\u65b0\u3057\u3044 Trace ID \u3092\u751f\u6210\u3057\u307e\u3059\u3002<\/p>\n<p>Rails \u306e Request ID \u3068 Trace ID \u3092\u7d71\u4e00\u3059\u308b\u5b9f\u88c5\u3082\u884c\u3044\u307e\u3057\u305f\u3002<code>traceparent<\/code> \u304c\u3042\u308b\u5834\u5408\u306b\u306f\u3001\u305d\u308c\u3092\u512a\u5148\u3057\u3066\u3044\u308b\u3060\u3051\u306a\u306e\u3067\u3001\u4ed5\u7d44\u307f\u306f\u540c\u3058\u3067\u3059\u3002<\/p>\n<p>\u3053\u308c\u306b\u3088\u308a\u3001\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30ed\u30b0\u51fa\u529b\u3067\u3082 Trace ID \u304c\u4f7f\u308f\u308c\u308b\u3088\u3046\u306b\u306a\u308a\u3001\u4e00\u8cab\u6027\u306e\u3042\u308b\u30ed\u30b0\u51fa\u529b\u304c\u53ef\u80fd\u306b\u306a\u308a\u307e\u3057\u305f\u3002<code>insert_hyphens<\/code> \u30e1\u30bd\u30c3\u30c9\u306f\u3001Trace ID \u306b\u30cf\u30a4\u30d5\u30f3\u3092\u633f\u5165\u3057\u3066 UUID \u5f62\u5f0f\u306b\u5909\u63db\u3059\u308b\u6a5f\u80fd\u3092\u6301\u3063\u3066\u3044\u307e\u3059:<\/p>\n<pre><code class=\"language-ruby\">def insert_hyphens(trace_id)\n  trace_id = trace_id.dup.to_s\n\n  # \u5404\u4f4d\u7f6e\u306b\u30cf\u30a4\u30d5\u30f3\u3092\u633f\u5165\uff08\u6587\u5b57\u5217\u9577\u3092\u30c1\u30a7\u30c3\u30af\u3057\u306a\u304c\u3089\uff09\n  [8, 13, 18, 23].each do |pos|\n    break if trace_id.length &lt;= pos\n    trace_id.insert(pos, &quot;-&quot;)\n  end\n\n  trace_id\nend<\/code><\/pre>\n<p>\u3053\u306e\u5b9f\u88c5\u306f\u3001<code>ActionDispatch::RequestId<\/code> \u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u76f4\u5f8c\u306b\u914d\u7f6e\u3059\u308b\u3053\u3068\u3067\u3001Rails \u304c\u751f\u6210\u3057\u305f <code>request_id<\/code> \u3092\u4e0a\u66f8\u304d\u3057\u307e\u3059\u3002\u3053\u308c\u306f bisque \u306e\u8a18\u4e8b<a href=\"https:\/\/zenn.dev\/bisque\/scraps\/e0c58eb6fd07fa\">Rails\u306e\u30ed\u30b0\u306b\u542b\u307e\u308c\u308brequest_id\u306b\u3064\u3044\u3066\u30b3\u30fc\u30c9\u30ea\u30fc\u30c7\u30a3\u30f3\u30b0\u3057\u305f\u30e1\u30e2<\/a>\u3067\u89e3\u8aac\u3055\u308c\u3066\u3044\u308b Rails \u306e <code>request_id<\/code> \u306e\u4ed5\u7d44\u307f\u3092\u5229\u7528\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n<p><code>bin\/rails middleware<\/code> \u30b3\u30de\u30f3\u30c9\u3092\u5b9f\u884c\u3059\u308b\u3068\u3001\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u9806\u5e8f\u3092\u78ba\u8a8d\u3067\u304d\u308b\u3093\u3067\u3059\u306d\u3002<\/p>\n<h3>3. \u30ab\u30b9\u30bf\u30e0\u30ed\u30b0\u30d5\u30a9\u30fc\u30de\u30c3\u30bf\u306e\u5b9f\u88c5<\/h3>\n<p>\u6b21\u306b\u3001SemanticLogger \u306e\u30d5\u30a9\u30fc\u30de\u30c3\u30bf\u3092\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u3057\u3001Trace ID \u3092\u30ed\u30b0\u306e\u30c8\u30c3\u30d7\u30ec\u30d9\u30eb\u30d5\u30a3\u30fc\u30eb\u30c9\u3068\u3057\u3066\u51fa\u529b\u3067\u304d\u308b\u3088\u3046\u306b\u3057\u307e\u3057\u305f\u3002\u3053\u308c\u306f en30 \u306e\u8a18\u4e8b<a href=\"https:\/\/zenn.dev\/en30\/articles\/b48f48ff4710d9\">Rails on GKE\u3067\u3082\u7406\u60f3\u306e\u69cb\u9020\u5316\u30ed\u30b0\u3092\u76ee\u6307\u3059<\/a>\u3067\u7d39\u4ecb\u3055\u308c\u3066\u3044\u308b\u30a2\u30d7\u30ed\u30fc\u30c1\u3092\u53c2\u8003\u306b\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n<p><a href=\"https:\/\/github.com\/takotakot\/rails8-google-cloud-example\/blob\/a4e0f4c95dfd0dfcb6e81b64ca1ce06ce43d5791\/structured-logging\/lib\/formatters\/cloud_trace_log_json.rb\">cloud_trace_log_json.rb<\/a><\/p>\n<pre><code class=\"language-ruby:lib\/formatters\/cloud_trace_log_json.rb\"># lib\/formatters\/cloud_trace_log_json.rb\nmodule Formatters\n  class CloudTraceLogJson &lt; SemanticLogger::Formatters::Raw\n    # Default JSON time format is ISO8601\n    def initialize(time_format: :iso_8601, time_key: :timestamp, **args)\n      super(time_format: time_format, time_key: time_key, **args)\n    end\n\n    def traceparent\n      if log.named_tags &amp;&amp; log.named_tags[:traceparent]\n        hash[:traceparent] = log.named_tags[:traceparent]\n      end\n    end\n\n    def trace\n      if log.named_tags &amp;&amp; log.named_tags[:trace_id]\n        trace_id = log.named_tags[:trace_id]\n      elsif log.named_tags &amp;&amp; log.named_tags[:request_id]\n        trace_id = log.named_tags[:request_id].gsub(&quot;-&quot;, &quot;&quot;)\n      else\n        return\n      end\n\n      hash[&quot;logging.googleapis.com\/trace&quot;] = &quot;projects\/#{ENV[&quot;PROJECT_ID&quot;]}\/traces\/#{trace_id}&quot; if trace_id &amp;&amp; !ENV[&quot;PROJECT_ID&quot;].nil? &amp;&amp; !ENV[&quot;PROJECT_ID&quot;].empty?\n    end\n\n    # ...\n  end\nend<\/code><\/pre>\n<p>\u3053\u306e\u30d5\u30a9\u30fc\u30de\u30c3\u30bf\u306b\u3088\u308a\u3001\u30ed\u30b0\u51fa\u529b\u6642\u306b Trace ID \u304c Google Cloud Logging \u304c\u6c42\u3081\u308b\u5f62\u5f0f <a href=\"https:\/\/cloud.google.com\/logging\/docs\/structured-logging?hl=ja\">\u69cb\u9020\u5316\u30ed\u30ae\u30f3\u30b0<\/a> \u306b\u5909\u63db\u3055\u308c\u3001UI \u3067\u95a2\u9023\u3059\u308b\u30ed\u30b0\u3092 Trace ID \u3067\u691c\u7d22\u3067\u304d\u308b\u3088\u3046\u306b\u306a\u308a\u307e\u3059\u3002\u7279\u306b Google Cloud Logging \u56fa\u6709\u306e <code>logging.googleapis.com\/trace<\/code> \u30d5\u30a3\u30fc\u30eb\u30c9\u306b\u3001<code>PROJECT_ID<\/code> \u3092\u542b\u3081\u305f\u51fa\u529b\u3092\u3057\u3066\u3044\u307e\u3059\u3002\u3053\u308c\u3067\u3001Google Cloud \u306e\u30c8\u30ec\u30fc\u30b9\u6a5f\u80fd\u3068\u306e\u7d71\u5408\u304c\u5bb9\u6613\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n<p>\u53c2\u8003\u8a18\u4e8b\u3067\u306f\u3001Lograge \u3068 google-cloud-logging (ruby) \u3092\u7d44\u307f\u5408\u308f\u305b\u3066\u3044\u307e\u3057\u305f\u3002<\/p>\n<h3>4. Rails \u306e\u8a2d\u5b9a<\/h3>\n<p>\u5c0e\u5165\u306e\u305f\u3081\u3001\u3044\u304f\u3064\u304b Rails \u306e\u8a2d\u5b9a\u3092\u5909\u66f4\u3057\u307e\u3057\u305f\u3002\u7279\u306b\u3001SemanticLogger \u3092\u4f7f\u7528\u3059\u308b\u305f\u3081\u306e\u8a2d\u5b9a\u3092\u884c\u3044\u307e\u3057\u305f\u3002<code>config\/application.rb<\/code> \u306b\u4ee5\u4e0b\u3092\u8ffd\u52a0\u3057\u307e\u3059:<\/p>\n<h3>TaggedLogging<\/h3>\n<p>\u30ed\u30b0\u30a8\u30f3\u30c8\u30ea\u306b\u30bf\u30b0\u3092\u4ed8\u3051\u3066\u304a\u304d\u307e\u3059\u3002<code>log_tags<\/code> \u306b\u30cf\u30c3\u30b7\u30e5\u3092\u6307\u5b9a\u3059\u308b\u3068\u3001\u540d\u524d\u4ed8\u304d\u306e\u30bf\u30b0\u3092\u8ffd\u52a0\u3067\u304d\u307e\u3059\u3002\u3053\u308c\u306b\u3088\u308a\u3001\u30ea\u30af\u30a8\u30b9\u30c8 ID \u3084 Trace ID \u3092\u30ed\u30b0\u306b\u542b\u3081\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n<p><a href=\"https:\/\/github.com\/takotakot\/rails8-google-cloud-example\/blob\/a4e0f4c95dfd0dfcb6e81b64ca1ce06ce43d5791\/structured-logging\/config\/application.rb#L27\">application.rb<\/a><\/p>\n<pre><code class=\"language-ruby:config\/application.rb\"># config\/application.rb\nconfig.log_tags = {\n  request_id: :request_id,\n  trace_id: -&gt;(request) { request.env[:trace_id] },\n  traceparent: -&gt;(request) { request.env[:traceparent] }\n}<\/code><\/pre>\n<p>\u3053\u306e\u3088\u3046\u306b\u3001<code>log_tags<\/code> \u3092\u8a2d\u5b9a\u3059\u308b\u3068\u3001SemanticLogger \u3067\u306f <code>named_tags<\/code> \u30d5\u30a3\u30fc\u30eb\u30c9\u306b\u30bf\u30b0\u304c\u8ffd\u52a0\u3055\u308c\u307e\u3059\u3002\u3053\u308c\u306b\u3088\u308a\u3001\u30ea\u30af\u30a8\u30b9\u30c8 ID \u3084 Trace ID \u3092\u542b\u3080\u69cb\u9020\u5316\u30ed\u30b0\u3092\u751f\u6210\u3067\u304d\u307e\u3059\u3002<\/p>\n<p><code>config\/environments\/production.rb<\/code> \u306b\u306f <code>config.log_tags = [ :request_id ]<\/code> \u3068\u3044\u3046\u884c\u304c\u3042\u308a\u307e\u3059\u304c\u3001<code>config\/application.rb<\/code> \u306e\u8a2d\u5b9a\u3092\u4e0a\u66f8\u304d\u3057\u3066\u3057\u307e\u3044\u307e\u3059\u3002<code>config\/environments\/production.rb<\/code> \u306b\u3082\u540c\u69d8\u306e\u8a2d\u5b9a\u3092\u8ffd\u52a0\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002<\/p>\n<h3>\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u914d\u7f6e\u9806\u5e8f<\/h3>\n<p>\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u633f\u5165\u4f4d\u7f6e\u306f\u91cd\u8981\u306a\u691c\u8a0e\u4e8b\u9805\u3067\u3057\u305f\u3002<code>ActionDispatch::RequestId<\/code> \u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u76f4\u5f8c\u306b\u914d\u7f6e\u3059\u308b\u3053\u3068\u3067\u3001Request ID \u3092\u3059\u3050\u306b\u4e0a\u66f8\u304d\u3059\u308b\u3088\u3046\u306b\u3057\u307e\u3059\u3002\u3068\u306b\u304b\u304f\u6700\u521d\u306b\u62bd\u51fa\u3057\u305f\u3051\u308c\u3070 <code>insert_after 0<\/code> \u3067\u826f\u3044\u306e\u3067\u3059\u304c\u3001<code>RequestId<\/code> \u3068\u6574\u5408\u6027\u3092\u6301\u305f\u305b\u305f\u304b\u3063\u305f\u306e\u3067\u3001<code>ActionDispatch::RequestId<\/code> \u306e\u76f4\u5f8c\u306b\u914d\u7f6e\u3057\u307e\u3057\u305f\u3002<\/p>\n<pre><code class=\"language-ruby:config\/application.rb\"># config\/application.rb\n# config.middleware.insert_after 0, TraceMiddleware\nconfig.middleware.insert_after ActionDispatch::RequestId, TraceMiddleware<\/code><\/pre>\n<p>\u3053\u306e\u914d\u7f6e\u306b\u3088\u308a\u3001\u4ee5\u4e0b\u306e\u51e6\u7406\u30d5\u30ed\u30fc\u304c\u5b9f\u73fe\u3055\u308c\u307e\u3059:<\/p>\n<ol>\n<li><code>ActionDispatch::RequestId<\/code> \u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u304c\u30c7\u30d5\u30a9\u30eb\u30c8\u306e <code>request_id<\/code> \u3092\u751f\u6210<\/li>\n<li><code>TraceMiddleware<\/code> \u304c Trace ID \u3092\u62bd\u51fa\u3057\u3001\u5fc5\u8981\u306b\u5fdc\u3058\u3066 <code>request_id<\/code> \u3092\u4e0a\u66f8\u304d<\/li>\n<li><code>Rack::Logger<\/code> \u304c <code>request_id<\/code>\uff08\u4e0a\u66f8\u304d\u3055\u308c\u305f\u5834\u5408\u306f Trace ID\uff09\u3092\u30ed\u30b0\u306b\u51fa\u529b<\/li>\n<\/ol>\n<h3>SemanticLogger \u306e\u8a2d\u5b9a<\/h3>\n<p>SemanticLogger \u3092\u4f7f\u7528\u3057\u3001\u72ec\u81ea\u30d5\u30a9\u30fc\u30de\u30c3\u30bf\u3092\u5c0e\u5165\u3059\u308b\u305f\u3081\u306e\u8a2d\u5b9a\u3092\u884c\u3044\u307e\u3059\u3002<code>config\/application.rb<\/code> \u306b\u4ee5\u4e0b\u3092\u8ffd\u52a0\u3057\u307e\u3059:<\/p>\n<pre><code class=\"language-ruby:config\/application.rb\"># config\/application.rb\nconfig.rails_semantic_logger.started = true\nconfig.rails_semantic_logger.add_file_appender = false\n# config.semantic_logger.add_appender(io: $stdout, formatter: :json)\nconfig.semantic_logger.add_appender(io: $stdout, formatter: Formatters::CloudTraceLogJson.new)<\/code><\/pre>\n<p>\u958b\u767a\u74b0\u5883\u3067\u3082 JSON \u5f62\u5f0f\u3067\u51fa\u529b\u3055\u308c\u308b\u306e\u304c\u5acc\u306a\u5834\u5408\u306f\u3001<code>config\/environments\/production.rb<\/code> \u3060\u3051\u306b\u8ffd\u52a0\u3057\u3066\u304a\u304f\u3068\u826f\u3044\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002\uff08\u3054\u3081\u3093\u306a\u3055\u3044\u3001\u8a66\u3057\u3066\u3044\u307e\u305b\u3093\u3002\u958b\u767a\u74b0\u5883\u3067 JSON \u306b\u306a\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3057\u306a\u304c\u3089\u5b9f\u88c5\u3092\u3057\u305f\u306e\u3067\u3059\u2026\uff09<\/p>\n<h2>\u95b2\u89a7<\/h2>\n<p>Cloud Logging \u3067\u306f\u3001&quot;243 ms&quot; \u306e\u3088\u3046\u306a\u3001\u51e6\u7406\u6642\u9593\u3092\u8868\u3059\u30d5\u30a3\u30fc\u30eb\u30c9\uff08\u6a2a\u306b\u30c8\u30ec\u30fc\u30b9\u3092\u793a\u3059\u8a18\u53f7\u304c\u3064\u3044\u3066\u3044\u308b\uff09\u3092\u30af\u30ea\u30c3\u30af\u3059\u308b\u3068\u3001\u30e1\u30cb\u30e5\u30fc\u304c\u51fa\u3066\u304d\u3066 &quot;Show entries for this trace&quot; \u3092\u9078\u629e\u3059\u308b\u3068\u3001\u7279\u5b9a\u30c8\u30ec\u30fc\u30b9\u3060\u3051\u3092\u62bd\u51fa\u3057\u3066\u8868\u793a\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2025\/05\/cloud-logging_1.png\" alt=\"Cloud Logging 1\" \/><\/p>\n<p>Rails \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304b\u3089\u306e\u30ed\u30b0\u306f\u3001JSON \u5f62\u5f0f\u3067\u51fa\u529b\u3055\u308c\u3066\u3044\u307e\u3059\u3002<br \/>\n<img decoding=\"async\" src=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2025\/05\/cloud-logging_2.png\" alt=\"Cloud Logging JSON\" \/><\/p>\n<p>JSON \u5f62\u5f0f\u3067\u30ed\u30b0\u3092\u51fa\u529b\u3057\u305f\u5834\u5408\u3001&quot;logging.googleapis.com\/trace&quot; \u30d5\u30a3\u30fc\u30eb\u30c9\u306f\u3001jsonPayload \u306b\u306f\u542b\u307e\u308c\u305a\u3001\u30c8\u30c3\u30d7\u30ec\u30d9\u30eb\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\u3068\u3057\u3066\u51fa\u529b\u3055\u308c\u307e\u3059\u3002<br \/>\n<img decoding=\"async\" src=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2025\/05\/cloud-logging_3.png\" alt=\"Cloud Logging trace\" \/><\/p>\n<h2>\u304a\u307e\u3051: \u30c7\u30d7\u30ed\u30a4\u306e\u305f\u3081\u306e\u30b3\u30fc\u30c9<\/h2>\n<h3>Terraform \u3092\u4f7f\u7528\u3057\u305f\u30c7\u30d7\u30ed\u30a4<\/h3>\n<p>\u5b9f\u969b\u306b Cloud Run \u306b\u30c7\u30d7\u30ed\u30a4\u3059\u308b\u969b\u3001Terraform \u3092\u4f7f\u7528\u3057\u3066\u30a4\u30f3\u30d5\u30e9\u3092\u30b3\u30fc\u30c9\u5316\u3057\u307e\u3057\u305f\u3002<code>terraform\/environments\/stg<\/code> \u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306b\u914d\u7f6e\u3057\u3066\u3044\u307e\u3059\u3002<code>.tfvars<\/code> \u30d5\u30a1\u30a4\u30eb\u3092\u4f5c\u6210\u3057\u3001\u30d7\u30ed\u30b8\u30a7\u30af\u30c8 ID \u3068\u30d0\u30c3\u30af\u30a8\u30f3\u30c9\u30d0\u30b1\u30c3\u30c8\u540d\u3092\u6307\u5b9a\u3057\u307e\u3059\u3002<\/p>\n<pre><code class=\"language-terraform:.tfvars\">project_id          = &quot;project-id&quot;\nbackend_bucket_name = &quot;tfstate-bucket&quot;<\/code><\/pre>\n<p>\u3042\u3068\u306f <code>terraform apply -var-file=&quot;.tfvars&quot;<\/code> \u3092\u5b9f\u884c\u3059\u308b\u3060\u3051\u3067\u3001Cloud Run \u74b0\u5883\u304c\u69cb\u7bc9\u3055\u308c\u307e\u3059\u3002\u305f\u3060\u3057\u3001\u30a4\u30e1\u30fc\u30b8\u306e\u30d3\u30eb\u30c9\u3068\u3001Secret Manager \u3078\u306e\u30b7\u30fc\u30af\u30ec\u30c3\u30c8\u306e\u767b\u9332\u306f\u624b\u52d5\u3067\u884c\u3046\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002<\/p>\n<h3>\u30a4\u30e1\u30fc\u30b8\u306e\u30d3\u30eb\u30c9<\/h3>\n<p><code>PROJECT_ID<\/code> \u3092\u6307\u5b9a\u3057\u3066\u3001Cloud Build \u3092\u4f7f\u7528\u3057\u3066\u30a4\u30e1\u30fc\u30b8\u3092\u30d3\u30eb\u30c9\u3057\u307e\u3059\u3002<\/p>\n<pre><code class=\"language-sh:build_image.sh\">export PROJECT_ID=&quot;project-id&quot;\ngcloud builds submit . --project=$PROJECT_ID --config build_image.yaml<\/code><\/pre>\n<p>\u6b63\u3057\u3044\u66f8\u304d\u65b9\u304b\u81ea\u4fe1\u306f\u3042\u308a\u307e\u305b\u3093\u304c\u3001<a href=\"https:\/\/github.com\/takotakot\/rails8-google-cloud-example\/blob\/a4e0f4c95dfd0dfcb6e81b64ca1ce06ce43d5791\/structured-logging\/build_image.yaml\">build_image.yaml<\/a> \u3068\u3057\u3066\u7f6e\u3044\u3066\u304a\u304d\u307e\u3059\u3002<\/p>\n<h2>\u307e\u3068\u3081<\/h2>\n<p>\u3053\u306e\u5b9f\u88c5\u306b\u3088\u308a\u3001\u4ee5\u4e0b\u306e\u3053\u3068\u304c\u9054\u6210\u3055\u308c\u308c\u307e\u3057\u305f:<\/p>\n<ol>\n<li><strong>\u5206\u6563\u30c8\u30ec\u30fc\u30b7\u30f3\u30b0\u306e\u30b5\u30dd\u30fc\u30c8<\/strong> &#8211; W3C TraceContext \u306b\u6e96\u62e0\u3057\u305f\u30c8\u30ec\u30fc\u30b9\u60c5\u5831\u306e\u4f1d\u64ad\u304c\u53ef\u80fd\u306b<\/li>\n<li><strong>\u69cb\u9020\u5316\u30ed\u30b0\u306e\u5b9f\u73fe<\/strong> &#8211; JSON \u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3067\u306e\u51fa\u529b\u306b\u3088\u308a\u3001\u691c\u7d22\u30fb\u5206\u6790\u304c\u5bb9\u6613\u306b<\/li>\n<li><strong>Google Cloud Logging \u3068\u306e\u7d71\u5408<\/strong> &#8211;  Trace ID \u3092 Cloud Logging \u306e\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3067\u51fa\u529b\u3059\u308b\u3053\u3068\u3067\u3001\u30ed\u30b0\u306e\u30d5\u30a3\u30eb\u30bf\u30ea\u30f3\u30b0\u304c\u6539\u5584<\/li>\n<li><strong>\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u53ef\u89b3\u6e2c\u6027\u5411\u4e0a<\/strong> &#8211; \u30ea\u30af\u30a8\u30b9\u30c8\u5168\u4f53\u3092\u901a\u3057\u305f\u8ffd\u8de1\u304c\u53ef\u80fd\u306b<\/li>\n<li><strong>\u6a19\u6e96\u30ed\u30b0\u3068\u306e\u4e92\u63db\u6027<\/strong> &#8211; \u65e2\u5b58\u306e\u30ed\u30b0\u53ce\u96c6\u30c4\u30fc\u30eb\u3084\u5206\u6790\u30c4\u30fc\u30eb\u3068\u306e\u4e92\u63db\u6027\u3092\u7dad\u6301<\/li>\n<\/ol>\n<p>Rails 8 \u3067\u306e\u69cb\u9020\u5316\u30ed\u30b0\u5b9f\u88c5\u3092\u901a\u3058\u3066\u3001SemanticLogger \u3068\u30ab\u30b9\u30bf\u30e0\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u3092\u6d3b\u7528\u3057\u305f Trace ID \u7d71\u5408\u306e\u65b9\u6cd5\u3092\u7d39\u4ecb\u3057\u307e\u3057\u305f\u3002\u3053\u306e\u5b9f\u88c5\u306f\u3001\u30de\u30a4\u30af\u30ed\u30b5\u30fc\u30d3\u30b9\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u306b\u304a\u3051\u308b\u30ed\u30b0\u306e\u7d71\u5408\u3068\u8ffd\u8de1\u6027\u5411\u4e0a\u306b\u8ca2\u732e\u3057\u3001\u30c7\u30d0\u30c3\u30b0\u3084\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u5206\u6790\u306e\u52b9\u7387\u5316\u306b\u5f79\u7acb\u3061\u307e\u3059\u3002<\/p>\n<p>bisque \u306b\u3088\u308b\u30b3\u30fc\u30c9\u30ea\u30fc\u30c7\u30a3\u30f3\u30b0\u3067\u89e3\u8aac\u3055\u308c\u3066\u3044\u308b\u3088\u3046\u306b\u3001Rails \u306e request_id \u6a5f\u69cb\u306f\u3001\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u3001\u30b9\u30ec\u30c3\u30c9\u30ed\u30fc\u30ab\u30eb\u5909\u6570\u3001\u30bf\u30b0\u4ed8\u3051\u30ed\u30ac\u30fc\u306e\u9023\u643a\u306b\u3088\u308a\u5b9f\u73fe\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u4eca\u56de\u306e\u5b9f\u88c5\u3067\u306f\u3001\u305d\u306e\u4ed5\u7d44\u307f\u3092\u6d3b\u304b\u3057\u3064\u3064\u3001W3C TraceContext \u3068\u306e\u7d71\u5408\u3092\u56f3\u308a\u307e\u3057\u305f\u3002<\/p>\n<p>\u3053\u306e\u6587\u7ae0\u306e\u4e00\u90e8\u306f LLM \u306b\u66f8\u304b\u305b\u305f\u306e\u3067\u3059\u304c\u3001LLM \u306f\u300e\u4eca\u5f8c\u306e\u5c55\u671b\u3068\u3057\u3066\u306f\u3001\u3088\u308a\u8a73\u7d30\u306a\u30e1\u30c8\u30ea\u30af\u30b9\u306e\u53ce\u96c6\u3084\u30a2\u30e9\u30fc\u30c8\u8a2d\u5b9a\u3068\u306e\u9023\u643a\u306a\u3069\u3001\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u6a5f\u80fd\u306e\u5f37\u5316\u3092\u691c\u8a0e\u3057\u3066\u3044\u307e\u3059\u3002\u307e\u305f\u3001OpenTelemetry \u3068\u306e\u7d71\u5408\u3082\u8996\u91ce\u306b\u5165\u308c\u3066\u3044\u307e\u3059\u3002\u300f\u3068\u66f8\u3044\u3066\u3044\u307e\u3057\u305f\u3002\u305d\u3093\u306a\u3053\u3068\u3059\u308b\u304b\u306f\u5206\u304b\u308a\u307e\u305b\u3093\u3002<\/p>\n<p>\u4eba\u9593\u500b\u4eba\u3068\u3057\u3066\u306f\u3001\u4ed6\u306b\u3001<a href=\"https:\/\/zenn.dev\/knowledgework\/articles\/cloud-logging-special-payload-fields\">Cloud Logging \u69cb\u9020\u5316\u30ed\u30b0\u306e\u7279\u5225\u306a JSON \u30d5\u30a3\u30fc\u30eb\u30c9\u307e\u3068\u3081<\/a>\u3092\u53c2\u8003\u306b\u3057\u3066\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u8ffd\u52a0\u3057\u3066\u3082\u826f\u3044\u304b\u306a\u3041\u3068\u601d\u3044\u307e\u3057\u305f\u3002<\/p>\n<h2>\u53c2\u8003\u30ea\u30bd\u30fc\u30b9<\/h2>\n<ul>\n<li><a href=\"https:\/\/www.w3.org\/TR\/trace-context\/\">W3C Trace Context\u4ed5\u69d8<\/a><\/li>\n<li><a href=\"https:\/\/logger.rocketjob.io\/index.html\">Semantic Logger<\/a> \u516c\u5f0f\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8<\/li>\n<li>GitHub <a href=\"https:\/\/github.com\/reidmorrison\/semantic_logger\">SemanticLogger<\/a><\/li>\n<li>GitHub <a href=\"https:\/\/github.com\/reidmorrison\/rails_semantic_logger\">RailsSemanticLogger<\/a><\/li>\n<li><a href=\"https:\/\/cloud.google.com\/logging\/docs\">Google Cloud Logging\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8<\/a><\/li>\n<li><a href=\"https:\/\/zenn.dev\/en30\/articles\/b48f48ff4710d9\">Rails on GKE\u3067\u3082\u7406\u60f3\u306e\u69cb\u9020\u5316\u30ed\u30b0\u3092\u76ee\u6307\u3059<\/a> last modified: 2021-04-28\n<ul>\n<li>en30 \u306b\u3088\u308b Rails on GKE \u3067\u306e\u69cb\u9020\u5316\u30ed\u30b0\u5b9f\u88c5\u306b\u95a2\u3059\u308b\u8a18\u4e8b\u3002\u7279\u306b Google Cloud Logging \u3068\u306e\u7d71\u5408\u65b9\u6cd5\u306b\u3064\u3044\u3066\u53c2\u8003\u306b\u306a\u308a\u307e\u3059\u3002<\/li>\n<\/ul>\n<\/li>\n<li><a href=\"https:\/\/zenn.dev\/bisque\/scraps\/e0c58eb6fd07fa\">Rails\u306e\u30ed\u30b0\u306b\u542b\u307e\u308c\u308brequest_id\u306b\u3064\u3044\u3066\u30b3\u30fc\u30c9\u30ea\u30fc\u30c7\u30a3\u30f3\u30b0\u3057\u305f\u30e1\u30e2<\/a> 2022-09-28\n<ul>\n<li>bisque \u306b\u3088\u308b Rails \u306e request_id \u306e\u8a73\u7d30\u306a\u89e3\u8aac\u8a18\u4e8b\u3002ActionDispatch::RequestId \u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u3084 ActiveSupport::TaggedLogging \u306e\u5185\u90e8\u5b9f\u88c5\u306b\u3064\u3044\u3066\u8a73\u3057\u304f\u8aac\u660e\u3057\u3066\u3044\u307e\u3059\u3002<\/li>\n<\/ul>\n<\/li>\n<li><a href=\"https:\/\/takanamito.hateblo.jp\/entry\/2020\/12\/22\/100500\">Fargate\u4e0a\u306eRails\u304b\u3089JSON\u5f62\u5f0f\u3067\u30ed\u30b0\u3092request_id\u3068\u4e00\u7dd2\u306b\u51fa\u529b<\/a> 2020-12-22<\/li>\n<li><a href=\"https:\/\/qiita.com\/minamijoyo\/items\/7237efa140013dd53870\">lograge\u3067\u306fRails.logger\u306e\u30ed\u30b0\u51fa\u529b\u304cJSON\u5f62\u5f0f\u306b\u306a\u3089\u306a\u3044\u554f\u984c<\/a> last modified: 2020-08-12<\/li>\n<li><a href=\"https:\/\/gist.github.com\/tmimura39\/e0fa1a794021d84d303969a86884cdc1\">tmimura39\/rails_json_log_formatter.rb<\/a> 2020-11-13<\/li>\n<li><a href=\"https:\/\/zenn.dev\/knowledgework\/articles\/cloud-logging-special-payload-fields\">Cloud Logging \u69cb\u9020\u5316\u30ed\u30b0\u306e\u7279\u5225\u306a JSON \u30d5\u30a3\u30fc\u30eb\u30c9\u307e\u3068\u3081<\/a> 2023-10-23<\/li>\n<li>Cloud Logging <a href=\"https:\/\/cloud.google.com\/logging\/docs\/structured-logging?hl=ja\">\u69cb\u9020\u5316\u30ed\u30ae\u30f3\u30b0<\/a><\/li>\n<\/ul>\n<hr \/>\n<h1>The Path to Implementing Structured Logging in Rails 8<\/h1>\n<h2>Overview<\/h2>\n<ul>\n<li>The combination of structured logging and Cloud Logging is convenient\n<ul>\n<li>Filtering<\/li>\n<li>Grouping of a series of logs<\/li>\n<\/ul>\n<\/li>\n<li>Cloud Run\n<ul>\n<li>The <code>traceparent<\/code> header is added by default<\/li>\n<li>Trace ID is also included in the logs of the (hidden) LB<\/li>\n<li>Demonstrated with standard output<\/li>\n<\/ul>\n<\/li>\n<li>Execution Environment\n<ul>\n<li>Rails 8<\/li>\n<li>Ruby 3.4<\/li>\n<li>No DB<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>I thought that even in cases of a small start where not much time can be spent on non-functional aspects, having a preceding example including working code would be helpful as a reference for implementation.<\/p>\n<h2>Introduction<\/h2>\n<p>In this article, I will introduce an example of implementing structured logging in a Rails 8 application. In particular, I will focus on how to include Trace IDs for distributed tracing in logs, and explain how to utilize <a href=\"https:\/\/www.w3.org\/TR\/trace-context\/\">W3C TraceContext<\/a> in conjunction with <a href=\"https:\/\/logger.rocketjob.io\/\">SemanticLogger<\/a>(<a href=\"https:\/\/github.com\/reidmorrison\/semantic_logger\">GitHub<\/a>, <a href=\"https:\/\/github.com\/reidmorrison\/rails_semantic_logger\">Rails Semantic Logger<\/a>).<\/p>\n<p>I have the impression that <a href=\"https:\/\/github.com\/roidrage\/lograge\">Lograge<\/a> is often used, but I&#8217;ve also seen some articles mentioning that it&#8217;s cumbersome to convert to JSON and add a <code>request_id<\/code>. In that regard, SemanticLogger seemed to make it easy to achieve structured logging, so I tried implementing it, partly as an investigation.<\/p>\n<h2>Background<\/h2>\n<p>As microservice architecture becomes more prevalent, tracking requests across multiple services has become an important issue. W3C TraceContext is a standard for propagating trace information between services, using the HTTP <code>traceparent<\/code> header to pass Trace IDs.<\/p>\n<p>In this project, the goal was to integrate this Trace ID into the logs of the Rails application and enable centralized monitoring with Google Cloud Logging.<\/p>\n<h2>Summary<\/h2>\n<ol>\n<li>Implement new middleware to extract Trace ID from Request Header (<code>traceparent<\/code>)\n<ul>\n<li>Extract the <code>HTTP_TRACEPARENT<\/code> header and save it to <code>env[:traceparent]<\/code><\/li>\n<li>Save the Trace ID to <code>env[:trace_id]<\/code><\/li>\n<li>Overwrite <code>req.request_id<\/code> with the Trace ID<\/li>\n<\/ul>\n<\/li>\n<li>Use <code>config.log_tags<\/code> to tag logs with Request ID and Trace ID\n<ul>\n<li>Add <code>traceparent<\/code> and <code>trace_id<\/code> to <code>log_tags<\/code><\/li>\n<\/ul>\n<\/li>\n<li>Implement a formatter to output structured logs (JSON)\n<ul>\n<li>Almost the same implementation as <code>SemanticLogger::Formatters::Json<\/code><\/li>\n<li>Inherit from <code>SemanticLogger::Formatters::Raw<\/code><\/li>\n<li>Output the <code>traceparent<\/code> field as a top-level field<\/li>\n<li>Set the <code>logging.googleapis.com\/trace<\/code> field as a top-level field, and configure the Trace ID in the format <code>projects\/#{ENV[&quot;PROJECT_ID&quot;]}\/traces\/#{trace_id}<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p>With the above, grouping logs by Trace ID in Cloud Logging becomes possible. There is no need to install a dedicated log agent; structured logs are sent to Google Cloud Logging simply by outputting directly to Cloud Run&#8217;s standard output.<\/p>\n<hr \/>\n<h2>(Reference) How Request ID works in Rails<\/h2>\n<p>First, it is important to understand the mechanism of <code>request_id<\/code> included in standard Rails logs. According to <a href=\"https:\/\/zenn.dev\/bisque\/scraps\/e0c58eb6fd07fa\">Notes on code reading about request_id included in Rails logs<\/a> by bisque, <code>request_id<\/code> is processed as follows:<\/p>\n<ol>\n<li><strong>Generation<\/strong>: The <code>ActionDispatch::RequestId<\/code> middleware retrieves <code>X-Request-Id<\/code> from the Request Header (generates a UUID if not present)<\/li>\n<li><strong>Propagation<\/strong>: Saved as the <code>request_id<\/code> attribute of the request object and also set as <code>X-Request-Id<\/code> in the Response Header (Note: I think it might be a Request Header&#8230; but I don&#8217;t understand it well)<\/li>\n<li><strong>Log Output<\/strong>: The <code>Rack::Logger<\/code> middleware inserts <code>request_id<\/code> into logs via <code>ActiveSupport::TaggedLogging<\/code><\/li>\n<li><strong>Thread Management<\/strong>: <code>ActiveSupport::TaggedLogging<\/code> uses <code>Thread.current<\/code> to manage tags, making them accessible throughout the application<\/li>\n<\/ol>\n<p>We will implement the integration of Trace ID from W3C TraceContext into logs by applying this mechanism.<\/p>\n<p>The <code>X-Request-Id<\/code> header was used (and is still used) to track request traces. From a distributed tracing perspective, it is recommended to use the <code>traceparent<\/code> header, which includes the Trace ID.<\/p>\n<h2>Implementation Steps<\/h2>\n<h3>1. Installing Necessary Gems<\/h3>\n<p>First, I installed the gem for structured log output. Add the following to your <code>Gemfile<\/code>. It also includes a mechanism for outputting in JSON.<\/p>\n<pre><code class=\"language-ruby:Gemfile\">gem &quot;rails_semantic_logger&quot;<\/code><\/pre>\n<h3>2. Implementing Trace Middleware<\/h3>\n<p>To extract the Trace ID from the Request Header, I implemented dedicated middleware. This is a similar approach to Rails&#8217; <code>ActionDispatch::RequestId<\/code>.<\/p>\n<p>I&#8217;ll also include a link to the GitHub repository: <a href=\"https:\/\/github.com\/takotakot\/rails8-google-cloud-example\/blob\/a4e0f4c95dfd0dfcb6e81b64ca1ce06ce43d5791\/structured-logging\/lib\/trace_middleware.rb\">trace_middleware.rb<\/a><\/p>\n<pre><code class=\"language-ruby:lib\/trace_middleware.rb\"># lib\/trace_middleware.rb\nclass TraceMiddleware\n  def initialize(app)\n    @app = app\n  end\n\n  def call(env)\n    # Extract HTTP_TRACEPARENT header\n    trace_parent = extract_trace_parent(env)\n    if trace_parent\n      env[:traceparent] = trace_parent\n    end\n\n    # Extract trace_id from the header,\n    # if not present, use SecureRandom.uuid\n    env[:trace_id] = make_trace_id(trace_parent)\n\n    # Overwrite the request_id with the trace_id\n    req = ActionDispatch::Request.new env\n    req.request_id = insert_hyphens(env[:trace_id])\n\n    @app.call(env)\n  end\n\n  # ...\nend<\/code><\/pre>\n<p>This middleware extracts the Trace ID from the W3C TraceContext compliant <code>traceparent<\/code> header (saved in <code>HTTP_TRACEPARENT<\/code>) and stores it in the request environment variables. If the header does not exist, it generates a new Trace ID in UUID format.<\/p>\n<p>I also implemented a way to unify Rails&#8217; Request ID and Trace ID. If <code>traceparent<\/code> exists, it is prioritized, so the mechanism is the same.<\/p>\n<p>This allows the Trace ID to be used even in default log outputs, enabling consistent log output. The <code>insert_hyphens<\/code> method has the function of inserting hyphens into the Trace ID to convert it to UUID format:<\/p>\n<pre><code class=\"language-ruby\">def insert_hyphens(trace_id)\n  trace_id = trace_id.dup.to_s\n\n  # Insert hyphens at each position (while checking string length)\n  [8, 13, 18, 23].each do |pos|\n    break if trace_id.length &lt;= pos\n    trace_id.insert(pos, &quot;-&quot;)\n  end\n\n  trace_id\nend<\/code><\/pre>\n<p>This implementation is placed immediately after the <code>ActionDispatch::RequestId<\/code> middleware to overwrite the <code>request_id<\/code> generated by Rails. This utilizes the Rails <code>request_id<\/code> mechanism explained in bisque&#8217;s article <a href=\"https:\/\/zenn.dev\/bisque\/scraps\/e0c58eb6fd07fa\">Rails\u306e\u30ed\u30b0\u306b\u542b\u307e\u308c\u308brequest_id\u306b\u3064\u3044\u3066\u30b3\u30fc\u30c9\u30ea\u30fc\u30c7\u30a3\u30f3\u30b0\u3057\u305f\u30e1\u30e2(Notes on code reading about request_id included in Rails logs)<\/a>.<\/p>\n<p>It turns out you can check the order of middleware by running the <code>bin\/rails middleware<\/code> command.<\/p>\n<h3>3. Implementing a Custom Log Formatter<\/h3>\n<p>Next, I customized SemanticLogger&#8217;s formatter to output the Trace ID as a top-level field in the logs. This references the approach introduced in en30&#8217;s article <a href=\"https:\/\/zenn.dev\/en30\/articles\/b48f48ff4710d9\">Rails on GKE\u3067\u3082\u7406\u60f3\u306e\u69cb\u9020\u5316\u30ed\u30b0\u3092\u76ee\u6307\u3059(Aiming for Ideal Structured Logging even with Rails on GKE)<\/a>.<\/p>\n<p><a href=\"https:\/\/github.com\/takotakot\/rails8-google-cloud-example\/blob\/a4e0f4c95dfd0dfcb6e81b64ca1ce06ce43d5791\/structured-logging\/lib\/formatters\/cloud_trace_log_json.rb\">cloud_trace_log_json.rb<\/a><\/p>\n<pre><code class=\"language-ruby:lib\/formatters\/cloud_trace_log_json.rb\"># lib\/formatters\/cloud_trace_log_json.rb\nmodule Formatters\n  class CloudTraceLogJson &lt; SemanticLogger::Formatters::Raw\n    # Default JSON time format is ISO8601\n    def initialize(time_format: :iso_8601, time_key: :timestamp, **args)\n      super(time_format: time_format, time_key: time_key, **args)\n    end\n\n    def traceparent\n      if log.named_tags &amp;&amp; log.named_tags[:traceparent]\n        hash[:traceparent] = log.named_tags[:traceparent]\n      end\n    end\n\n    def trace\n      if log.named_tags &amp;&amp; log.named_tags[:trace_id]\n        trace_id = log.named_tags[:trace_id]\n      elsif log.named_tags &amp;&amp; log.named_tags[:request_id]\n        trace_id = log.named_tags[:request_id].gsub(&quot;-&quot;, &quot;&quot;)\n      else\n        return\n      end\n\n      hash[&quot;logging.googleapis.com\/trace&quot;] = &quot;projects\/#{ENV[&quot;PROJECT_ID&quot;]}\/traces\/#{trace_id}&quot; if trace_id &amp;&amp; !ENV[&quot;PROJECT_ID&quot;].nil? &amp;&amp; !ENV[&quot;PROJECT_ID&quot;].empty?\n    end\n\n    # ...\n  end\nend<\/code><\/pre>\n<p>With this formatter, when logs are output, the Trace ID is converted to the format required by Google Cloud Logging <a href=\"https:\/\/cloud.google.com\/logging\/docs\/structured-logging\">Structured logging<\/a>, allowing related logs to be searched by Trace ID in the UI. In particular, it outputs to the Google Cloud Logging specific <code>logging.googleapis.com\/trace<\/code> field, including the <code>PROJECT_ID<\/code>. This facilitates integration with Google Cloud&#8217;s tracing features.<\/p>\n<p>The reference article combined Lograge and google-cloud-logging (ruby).<\/p>\n<h3>4. Rails Configuration<\/h3>\n<p>For introduction, I changed some Rails settings. In particular, I configured settings for using SemanticLogger. Add the following to <code>config\/application.rb<\/code>:<\/p>\n<h3>TaggedLogging<\/h3>\n<p>Add tags to log entries. If you specify a hash for <code>log_tags<\/code>, you can add named tags. This allows you to include Request ID and Trace ID in the logs.<\/p>\n<p><a href=\"https:\/\/github.com\/takotakot\/rails8-google-cloud-example\/blob\/a4e0f4c95dfd0dfcb6e81b64ca1ce06ce43d5791\/structured-logging\/config\/application.rb#L27\">application.rb<\/a><\/p>\n<pre><code class=\"language-ruby:config\/application.rb\"># config\/application.rb\nconfig.log_tags = {\n  request_id: :request_id,\n  trace_id: -&gt;(request) { request.env[:trace_id] },\n  traceparent: -&gt;(request) { request.env[:traceparent] }\n}<\/code><\/pre>\n<p>By setting <code>log_tags<\/code> in this way, tags are added to the <code>named_tags<\/code> field in SemanticLogger. This allows you to generate structured logs that include Request ID and Trace ID.<\/p>\n<p><code>config\/environments\/production.rb<\/code> has a line <code>config.log_tags = [ :request_id ]<\/code>, but this will overwrite the settings in <code>config\/application.rb<\/code>. You need to add similar settings to <code>config\/environments\/production.rb<\/code> as well.<\/p>\n<h3>Middleware Placement Order<\/h3>\n<p>The insertion position of the middleware was an important consideration. By placing it just after the <code>ActionDispatch::RequestId<\/code> middleware, the Request ID is overwritten immediately. If you just want to extract it first, <code>insert_after 0<\/code> would be fine, but I wanted to maintain consistency with <code>RequestId<\/code>, so I placed it just after <code>ActionDispatch::RequestId<\/code>.<\/p>\n<pre><code class=\"language-ruby:config\/application.rb\"># config\/application.rb\n# config.middleware.insert_after 0, TraceMiddleware\nconfig.middleware.insert_after ActionDispatch::RequestId, TraceMiddleware<\/code><\/pre>\n<p>This placement achieves the following processing flow:<\/p>\n<ol>\n<li>The <code>ActionDispatch::RequestId<\/code> middleware generates the default <code>request_id<\/code><\/li>\n<li><code>TraceMiddleware<\/code> extracts the Trace ID and overwrites <code>request_id<\/code> if necessary<\/li>\n<li><code>Rack::Logger<\/code> outputs <code>request_id<\/code> (or Trace ID if overwritten) to the log<\/li>\n<\/ol>\n<h3>SemanticLogger Configuration<\/h3>\n<p>Configure settings to use SemanticLogger and introduce a custom formatter. Add the following to <code>config\/application.rb<\/code>:<\/p>\n<pre><code class=\"language-ruby:config\/application.rb\"># config\/application.rb\nconfig.rails_semantic_logger.started = true\nconfig.rails_semantic_logger.add_file_appender = false\n# config.semantic_logger.add_appender(io: $stdout, formatter: :json)\nconfig.semantic_logger.add_appender(io: $stdout, formatter: Formatters::CloudTraceLogJson.new)<\/code><\/pre>\n<p>If you don&#8217;t want JSON format output in the development environment, it might be good to add this only to <code>config\/environments\/production.rb<\/code>. (I&#8217;m sorry, I haven&#8217;t tried it. I implemented it while confirming that it becomes JSON in the development environment&#8230;)<\/p>\n<h2>Viewing<\/h2>\n<p>In Cloud Logging, if you click on a field representing processing time, like &quot;243 ms&quot; (which has a trace symbol next to it), a menu will appear, and selecting &quot;Show entries for this trace&quot; allows you to extract and display only that specific trace.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2025\/05\/cloud-logging_1.png\" alt=\"Cloud Logging 1\" \/><\/p>\n<p>Logs from the Rails application are output in JSON format.<br \/>\n<img decoding=\"async\" src=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2025\/05\/cloud-logging_2.png\" alt=\"Cloud Logging JSON\" \/><\/p>\n<p>When logs are output in JSON format, the &quot;logging.googleapis.com\/trace&quot; field is not included in <code>jsonPayload<\/code> but is output as a top-level field.<br \/>\n<img decoding=\"async\" src=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2025\/05\/cloud-logging_3.png\" alt=\"Cloud Logging trace\" \/><\/p>\n<h2>Bonus: Code for Deployment<\/h2>\n<h3>Deployment using Terraform<\/h3>\n<p>When actually deploying to Cloud Run, I used Terraform to codify the infrastructure. It is placed in the <code>terraform\/environments\/stg<\/code> directory. Create a <code>.tfvars<\/code> file and specify the project ID and backend bucket name.<\/p>\n<pre><code class=\"language-terraform:.tfvars\">project_id          = &quot;project-id&quot;\nbackend_bucket_name = &quot;tfstate-bucket&quot;<\/code><\/pre>\n<p>Then, just by running <code>terraform apply -var-file=&quot;.tfvars&quot;<\/code>, the Cloud Run environment will be built. However, building the image and registering secrets to Secret Manager need to be done manually.<\/p>\n<h3>Image Building<\/h3>\n<p>Specify the <code>PROJECT_ID<\/code> and build the image using Cloud Build.<\/p>\n<pre><code class=\"language-sh:build_image.sh\">export PROJECT_ID=&quot;project-id&quot;\ngcloud builds submit . --project=$PROJECT_ID --config build_image.yaml<\/code><\/pre>\n<p>I&#8217;m not sure if this is the correct way to write it, but I&#8217;ll put it as <a href=\"https:\/\/github.com\/takotakot\/rails8-google-cloud-example\/blob\/a4e0f4c95dfd0dfcb6e81b64ca1ce06ce43d5791\/structured-logging\/build_image.yaml\">build_image.yaml<\/a>.<\/p>\n<h2>Summary<\/h2>\n<p>With this implementation, the following were achieved:<\/p>\n<ol>\n<li><strong>Support for Distributed Tracing<\/strong> &#8211; Propagation of trace information compliant with W3C TraceContext becomes possible<\/li>\n<li><strong>Realization of Structured Logging<\/strong> &#8211; Output in JSON format makes searching and analysis easier<\/li>\n<li><strong>Integration with Google Cloud Logging<\/strong> &#8211; Outputting Trace ID in Cloud Logging&#8217;s format improves log filtering<\/li>\n<li><strong>Improved Application Observability<\/strong> &#8211; Tracking across the entire request becomes possible<\/li>\n<li><strong>Compatibility with Standard Logs<\/strong> &#8211; Maintains compatibility with existing log collection and analysis tools<\/li>\n<\/ol>\n<p>Through the implementation of structured logging in Rails 8, I introduced a method for Trace ID integration utilizing SemanticLogger and custom middleware. This implementation contributes to the integration of logs and improvement of traceability in microservice architecture, and is useful for streamlining debugging and performance analysis.<\/p>\n<p>As explained in the code reading by bisque, Rails&#8217; <code>request_id<\/code> mechanism is realized by the cooperation of middleware, thread-local variables, and tagged loggers. In this implementation, while utilizing that mechanism, I aimed to integrate it with W3C TraceContext.<\/p>\n<p>Part of this article was written by an LLM, and the LLM wrote, &quot;As for future prospects, we are considering enhancing monitoring functions, such as collecting more detailed metrics and linking with alert settings. We are also considering integration with OpenTelemetry.&quot; I don&#8217;t know if I will do such things.<\/p>\n<p>As an individual human, I also thought it might be good to add fields by referring to <a href=\"https:\/\/zenn.dev\/knowledgework\/articles\/cloud-logging-special-payload-fields\">Cloud Logging \u69cb\u9020\u5316\u30ed\u30b0\u306e\u7279\u5225\u306a JSON \u30d5\u30a3\u30fc\u30eb\u30c9\u307e\u3068\u3081(Summary of Special JSON Fields for Cloud Logging Structured Logs)<\/a>.<\/p>\n<h2>Reference Resources<\/h2>\n<ul>\n<li><a href=\"https:\/\/www.w3.org\/TR\/trace-context\/\">W3C Trace Context Specification<\/a><\/li>\n<li><a href=\"https:\/\/logger.rocketjob.io\/index.html\">Semantic Logger<\/a> Official Documentation<\/li>\n<li>GitHub <a href=\"https:\/\/github.com\/reidmorrison\/semantic_logger\">SemanticLogger<\/a><\/li>\n<li>GitHub <a href=\"https:\/\/github.com\/reidmorrison\/rails_semantic_logger\">RailsSemanticLogger<\/a><\/li>\n<li><a href=\"https:\/\/cloud.google.com\/logging\/docs\">Google Cloud Logging Documentation<\/a><\/li>\n<li><a href=\"https:\/\/zenn.dev\/en30\/articles\/b48f48ff4710d9\">Rails on GKE\u3067\u3082\u7406\u60f3\u306e\u69cb\u9020\u5316\u30ed\u30b0\u3092\u76ee\u6307\u3059(Aiming for Ideal Structured Logging even with Rails on GKE)<\/a> last modified: 2021-04-28\n<ul>\n<li>An article by en30 regarding structured logging implementation with Rails on GKE. Particularly helpful for how to integrate with Google Cloud Logging.<\/li>\n<\/ul>\n<\/li>\n<li><a href=\"https:\/\/zenn.dev\/bisque\/scraps\/e0c58eb6fd07fa\">Rails\u306e\u30ed\u30b0\u306b\u542b\u307e\u308c\u308brequest_id\u306b\u3064\u3044\u3066\u30b3\u30fc\u30c9\u30ea\u30fc\u30c7\u30a3\u30f3\u30b0\u3057\u305f\u30e1\u30e2(Notes on code reading about request_id included in Rails logs)<\/a> 2022-09-28\n<ul>\n<li>A detailed explanatory article by bisque on Rails&#8217; <code>request_id<\/code>. It explains in detail the internal implementation of <code>ActionDispatch::RequestId<\/code> middleware and <code>ActiveSupport::TaggedLogging<\/code>.<\/li>\n<\/ul>\n<\/li>\n<li><a href=\"https:\/\/takanamito.hateblo.jp\/entry\/2020\/12\/22\/100500\">Fargate\u4e0a\u306eRails\u304b\u3089JSON\u5f62\u5f0f\u3067\u30ed\u30b0\u3092request_id\u3068\u4e00\u7dd2\u306b\u51fa\u529b(Outputting logs in JSON format with request_id from Rails on Fargate)<\/a> 2020-12-22<\/li>\n<li><a href=\"https:\/\/qiita.com\/minamijoyo\/items\/7237efa140013dd53870\">lograge\u3067\u306fRails.logger\u306e\u30ed\u30b0\u51fa\u529b\u304cJSON\u5f62\u5f0f\u306b\u306a\u3089\u306a\u3044\u554f\u984c(The issue where Rails.logger log output does not become JSON format with lograge)<\/a> last modified: 2020-08-12<\/li>\n<li><a href=\"https:\/\/gist.github.com\/tmimura39\/e0fa1a794021d84d303969a86884cdc1\">tmimura39\/rails_json_log_formatter.rb<\/a> 2020-11-13<\/li>\n<li><a href=\"https:\/\/zenn.dev\/knowledgework\/articles\/cloud-logging-special-payload-fields\">Cloud Logging \u69cb\u9020\u5316\u30ed\u30b0\u306e\u7279\u5225\u306a JSON \u30d5\u30a3\u30fc\u30eb\u30c9\u307e\u3068\u3081(Summary of Special JSON Fields for Cloud Logging Structured Logs)<\/a> 2023-10-23<\/li>\n<li>Cloud Logging <a href=\"https:\/\/cloud.google.com\/logging\/docs\/structured-logging\">Structured Logging<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Rails 8 \u3067\u306e\u69cb\u9020\u5316\u30ed\u30b0\u5b9f\u88c5\u306e\u8ecc\u8de1 English follows Japanese. \u6982\u8981 \u69cb\u9020\u5316\u30ed\u30b0\u3068 Cloud Logging \u306e\u7d44\u307f\u5408\u308f\u305b\u306f\u3001\u4fbf\u5229 \u30d5\u30a3\u30eb\u30bf\u30ea\u30f3\u30b0 \u4e00\u9023\u306e\u30ed\u30b0\u306e\u30b0\u30eb\u30fc\u30d4\u30f3\u30b0 Cloud R [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[26,13,14],"tags":[],"class_list":["post-566","post","type-post","status-publish","format-standard","hentry","category-cloud-run","category-google-cloud","category-ruby"],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p4dIdP-98","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/posts\/566","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/comments?post=566"}],"version-history":[{"count":3,"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/posts\/566\/revisions"}],"predecessor-version":[{"id":572,"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/posts\/566\/revisions\/572"}],"wp:attachment":[{"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/media?parent=566"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/categories?post=566"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/tags?post=566"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}