{"id":637,"date":"2026-01-24T22:08:51","date_gmt":"2026-01-24T13:08:51","guid":{"rendered":"https:\/\/tako.nakano.net\/blog\/?p=637"},"modified":"2026-01-24T22:33:46","modified_gmt":"2026-01-24T13:33:46","slug":"apache_structured_logging_cloud_run","status":"publish","type":"post","link":"https:\/\/tako.nakano.net\/blog\/2026\/01\/apache_structured_logging_cloud_run\/","title":{"rendered":"Cloud Run \u4e0a\u306e Apache \u3067\u69cb\u9020\u5316\u30ed\u30b0 (JSON) \u3068 Trace ID \u9023\u643a\u3092\u5b9f\u73fe\u3059\u308b"},"content":{"rendered":"<h1>Cloud Run \u4e0a\u306e Apache \u3067\u69cb\u9020\u5316\u30ed\u30b0 (JSON) \u3068 Trace ID \u9023\u643a\u3092\u5b9f\u73fe\u3059\u308b<\/h1>\n<p>English follows Japanese.<\/p>\n<h2>\u6982\u8981<\/h2>\n<ul>\n<li>Cloud Run \u3084 GKE \u3067 Apache \u3092\u52d5\u304b\u3059\u969b\u3001\u30ed\u30b0\u3092 JSON \u5f62\u5f0f\uff08\u69cb\u9020\u5316\u30ed\u30b0\uff09\u3067\u51fa\u529b\u3057\u305f\u3044<\/li>\n<li>Cloud Logging \u4e0a\u3067\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u4e00\u5143\u7ba1\u7406\u3059\u308b\u305f\u3081\u3001Trace ID \u3084 Span ID \u3092\u30ed\u30b0\u306b\u542b\u3081\u305f\u3044<\/li>\n<li>\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\uff08PHP\u306a\u3069\uff09\u3060\u3051\u3067\u306a\u304f\u3001Web \u30b5\u30fc\u30d0\u30fc\u306e\u30ed\u30b0\u3082\u540c\u69d8\u306b\u6271\u3044\u305f\u3044<\/li>\n<li>\u8ffd\u52a0\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u3084\u30b5\u30a4\u30c9\u30ab\u30fc\u3092\u4f7f\u308f\u305a\u3001Apache \u306e\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u3060\u3051\u3067\u5b9f\u73fe\u3059\u308b<\/li>\n<\/ul>\n<h2>\u306f\u3058\u3081\u306b<\/h2>\n<p>Cloud Run \u306a\u3069\u306e\u30b3\u30f3\u30c6\u30ca\u74b0\u5883\u3067 Web \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u904b\u7528\u3059\u308b\u5834\u5408\u3001\u30ed\u30b0\u306e\u53ef\u89b3\u6e2c\u6027\u306f\u975e\u5e38\u306b\u91cd\u8981\u3067\u3059\u3002<a href=\"https:\/\/tako.nakano.net\/blog\/2025\/05\/structured-logging-in-rails-8\/\">\u4ee5\u524d\u306e\u8a18\u4e8b<\/a> \u3067\u306f Ruby on Rails \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30ed\u30b0\u3092\u69cb\u9020\u5316\u3057\u3001Trace ID \u3092\u9023\u643a\u3055\u305b\u308b\u65b9\u6cd5\u3092\u7d39\u4ecb\u3057\u307e\u3057\u305f\u3002<\/p>\n<p>\u4eca\u56de\u306f\u3001WordPress \u3084 PHP \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u524d\u6bb5\u3068\u3057\u3066\u52d5\u4f5c\u3059\u308b\u3053\u3068\u304c\u591a\u3044 <strong>Apache HTTP Server<\/strong> \u306b\u7126\u70b9\u3092\u5f53\u3066\u307e\u3059\u3002Apache \u306e\u30a2\u30af\u30bb\u30b9\u30ed\u30b0\u3084\u30a8\u30e9\u30fc\u30ed\u30b0\u3082 JSON \u5f62\u5f0f\u3067\u51fa\u529b\u3057\u3001\u304b\u3064 Google Cloud \u306e Trace ID \u3092\u57cb\u3081\u8fbc\u3080\u3053\u3068\u3067\u3001Cloud Logging \u4e0a\u3067\u306e\u30ed\u30b0\u8abf\u67fb\u3092\u5287\u7684\u306b\u5feb\u9069\u306b\u3059\u308b\u65b9\u6cd5\u3092\u7d39\u4ecb\u3057\u307e\u3059\u3002<\/p>\n<p>Nginx \u3092\u4f7f\u3063\u3066\u3044\u308b\u5834\u5408\u3082\u540c\u69d8\u306e\u30a2\u30d7\u30ed\u30fc\u30c1\u304c\u53ef\u80fd\u3067\u3059\u3002<code>log_format json escape=json<\/code> \u7b49\u3092\u4f7f\u3046\u3053\u3068\u304c\u3067\u304d\u3001Apache \u3088\u308a\u3082\u8a2d\u5b9a\u304c\u30b7\u30f3\u30d7\u30eb\u3067\u3059\u3002\u672c\u8a18\u4e8b\u3067\u306f Apache \u306b\u7279\u5316\u3057\u3066\u8aac\u660e\u3057\u307e\u3059\u3002<\/p>\n<p>\u307e\u305f\u3001Cloud Run \u3067\u306f\u306a\u304f\u3001Google Cloud Load Balancing (GCLB) \u306e\u80cc\u5f8c\u3067 Apache \u3092\uff08\u4f8b\u3048\u3070 GKE \u4e0a\u3067\uff09\u52d5\u304b\u3057\u3066\u3044\u308b\u5834\u5408\u306b\u3082\u540c\u69d8\u306e\u30a2\u30d7\u30ed\u30fc\u30c1\u304c\u53ef\u80fd\u3067\u3059\u3002\u4eca\u56de\u306f Cloud Run \u3092\u524d\u63d0\u306b\u8aac\u660e\u3057\u307e\u3059\u3002<\/p>\n<p>\u79c1\u304c\u63d0\u5531\u3057\u305f Zero Scale WordPress \u3068\u3044\u3046\u3001DB \u3092\u30bc\u30ed\u30b9\u30b1\u30fc\u30eb\u3055\u305b\u308b WordPress \u306e\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u3067\u3082\u540c\u69d8\u306e\u8a2d\u5b9a\u3092\u4f7f\u3063\u3066\u3044\u307e\u3059\u3002\u53c2\u8003\u307e\u3067\u306b\u3001\u4ee5\u4e0b\u306e\u30ea\u30dd\u30b8\u30c8\u30ea\u306b\u3042\u308b\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u4f8b\u3082\u3054\u89a7\u304f\u3060\u3055\u3044\u3002<br \/>\n<a href=\"https:\/\/github.com\/takotakot\/zeroscale-wp\/blob\/main\/misc\/other-vhosts-access-log.conf\">https:\/\/github.com\/takotakot\/zeroscale-wp\/blob\/main\/misc\/other-vhosts-access-log.conf<\/a> \u53ca\u3073 <a href=\"https:\/\/github.com\/takotakot\/zeroscale-wp\/blob\/main\/Dockerfile\">https:\/\/github.com\/takotakot\/zeroscale-wp\/blob\/main\/Dockerfile<\/a><\/p>\n<h2>\u80cc\u666f\u3068\u8ab2\u984c<\/h2>\n<p>\u30c7\u30d5\u30a9\u30eb\u30c8\u306e Apache \u306e\u30ed\u30b0\u5f62\u5f0f (Combined Log Format) \u306f\u30d7\u30ec\u30fc\u30f3\u30c6\u30ad\u30b9\u30c8\u3067\u3042\u308a\u3001Cloud Logging \u306b\u9001\u3089\u308c\u305f\u5834\u5408\u3001<code>textPayload<\/code> \u3068\u3057\u3066\u6271\u308f\u308c\u307e\u3059\u3002\u3053\u308c\u306b\u306f2\u3064\u306e\u4e0d\u4fbf\u306a\u70b9\u304c\u3042\u308a\u307e\u3059\u3002<\/p>\n<ol>\n<li><strong>\u30af\u30a8\u30ea\u306e\u96e3\u3057\u3055<\/strong>: \u30b9\u30c6\u30fc\u30bf\u30b9\u30b3\u30fc\u30c9\u3084\u30ec\u30b9\u30dd\u30f3\u30b9\u30bf\u30a4\u30e0\u3067\u30d5\u30a3\u30eb\u30bf\u30ea\u30f3\u30b0\u3059\u308b\u969b\u3001\u6b63\u898f\u8868\u73fe\u306a\u3069\u3092\u4f7f\u3046\u5fc5\u8981\u304c\u3042\u308a\u9762\u5012<\/li>\n<li><strong>\u30c8\u30ec\u30fc\u30b9\u306e\u5206\u65ad<\/strong>: Cloud Run \u306f\u30ea\u30af\u30a8\u30b9\u30c8\u6bce\u306b Trace ID \u3092\u4ed8\u4e0e\u3057\u307e\u3059\u304c\u3001Apache \u306e\u6a19\u6e96\u30ed\u30b0\u306b\u306f\u3053\u308c\u304c\u542b\u307e\u308c\u307e\u305b\u3093\u3002\u305d\u306e\u305f\u3081\u3001\u7279\u5b9a\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u306b\u95a2\u9023\u3059\u308b\u30ed\u30b0\uff08Apache \u306e\u30a2\u30af\u30bb\u30b9\u30ed\u30b0 + \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30ed\u30b0\uff09\u3092\u307e\u3068\u3081\u3066\u8868\u793a\u3059\u308b\u3053\u3068\u304c\u96e3\u3057\u3044<\/li>\n<\/ol>\n<p>\u3053\u308c\u3089\u3092\u89e3\u6c7a\u3059\u308b\u305f\u3081\u306b\u3001Apache \u306e\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u3092\u5de5\u592b\u3057\u3066\u3001Google Cloud Logging \u306b\u9069\u3057\u305f JSON \u30ed\u30b0\u3092\u51fa\u529b\u3059\u308b\u3088\u3046\u306b\u3057\u307e\u3059\u3002<\/p>\n<h2>\u5b9f\u88c5\u306e\u30a2\u30d7\u30ed\u30fc\u30c1<\/h2>\n<p>\u5b9f\u88c5\u306e\u30dd\u30a4\u30f3\u30c8\u306f\u4ee5\u4e0b\u306e2\u70b9\u3067\u3059\u3002<\/p>\n<ol>\n<li><strong>Trace ID \u3068 Span ID \u306e\u62bd\u51fa<\/strong>: \u30ea\u30af\u30a8\u30b9\u30c8\u30d8\u30c3\u30c0\u30fc\uff08<code>traceparent<\/code> \u307e\u305f\u306f <code>X-Cloud-Trace-Context<\/code>\uff09\u304b\u3089 Trace ID \u3068 Span ID \u3092\u62bd\u51fa\u3057\u3001\u74b0\u5883\u5909\u6570\u306b\u8a2d\u5b9a<\/li>\n<li><strong>JSON \u30ed\u30b0\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u306e\u5b9a\u7fa9<\/strong>: <code>LogFormat<\/code> \u30c7\u30a3\u30ec\u30af\u30c6\u30a3\u30d6\u3092\u4f7f\u7528\u3057\u3066\u3001\u624b\u52d5\u3067 JSON \u6587\u5b57\u5217\u3092\u69cb\u7bc9<\/li>\n<\/ol>\n<h3>\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306e\u5b9f\u88c5<\/h3>\n<p>\u30d9\u30fc\u30b9\u3068\u306a\u308b\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\uff08\u4f8b\u3048\u3070 <code>other-vhosts-access-log.conf<\/code> \u3084\u30b5\u30a4\u30c8\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306a\u3069\uff09\u306b\u4ee5\u4e0b\u306e\u8a2d\u5b9a\u3092\u8ffd\u52a0\u3057\u307e\u3059\u3002<\/p>\n<p>\u3053\u306e\u8a2d\u5b9a\u3067\u306f\u3001<code>mod_setenvif<\/code> \u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u4f7f\u7528\u3057\u3066 Trace ID \u3068 Span ID \u3092\u62bd\u51fa\u3057\u3001\u74b0\u5883\u5909\u6570 <code>google_trace_id<\/code> \u306b\u683c\u7d0d\u3057\u3066\u3044\u307e\u3059\u3002<code>PROJECT_ID<\/code> \u3082\u3001\u76f4\u63a5\u6307\u5b9a\u3059\u308b\u3088\u308a\u3082\u3001\u74b0\u5883\u5909\u6570\u3068\u3057\u3066\u6e21\u3059\u65b9\u304c\u826f\u3044\u3067\u3057\u3087\u3046\u3002\u30b3\u30f3\u30c6\u30ca\u30a4\u30e1\u30fc\u30b8\u3092\u74b0\u5883\u6bce\u306b\u5206\u3051\u308b\u5fc5\u8981\u304c\u306a\u304f\u306a\u308a\u307e\u3059\u3002\u30a2\u30af\u30bb\u30b9\u30ed\u30b0\u306f\u30ea\u30af\u30a8\u30b9\u30c8\u306e\u8a18\u9332\u3067\u3042\u308b\u305f\u3081\u3001Severity \u306f INFO \u3068\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n<pre><code class=\"language-conf:other-vhosts-access-log.conf\"># JSON log format configuration\n# Allow breaking when User-Agent or Referer contains newline or double quotes.\n\n# Cloud Run \u4e0a\u3067 PROJECT_ID \u74b0\u5883\u5909\u6570\u304c\u5fc5\u8981\u3067\u3059\nPassEnv PROJECT_ID\n\n# Trace ID \u306e\u62bd\u51fa\n# \u30ec\u30ac\u30b7\u30fc\u306a X-Cloud-Trace-Context \u304b\u3089\u306e\u62bd\u51fa\uff08\u5ff5\u306e\u305f\u3081\uff09\nSetEnvIf X-Cloud-Trace-Context &quot;^([0-9a-f]{32})\/([0-9a-f]+).*$&quot; google_trace_id=$1 google_span_id=$2\n# W3C Trace Context (traceparent) \u304b\u3089\u306e\u62bd\u51fa\uff08\u3053\u3061\u3089\u304c\u512a\u5148\uff09\nSetEnvIf traceparent &quot;^00-([a-f0-9]{32})-([a-f0-9]{16})-.+$&quot; google_trace_id=$1 google_span_id=$2\n\n# \u4ed6\u306e\u4fbf\u5229\u5909\u6570\u306e\u8a2d\u5b9a\nSetEnvIf Remote_Addr &quot;^(.*)$&quot; real_client_ip=$1\nSetEnvIf X-Forwarded-For &quot;^([^,]+)&quot; real_client_ip=$1\nSetEnvIf Request_URI &quot;^(.*)$&quot; REQUEST_URI=$1\n\n# AccessLog JSON format\n# Cloud Logging \u304c\u89e3\u91c8\u3067\u304d\u308b\u30d5\u30a3\u30fc\u30eb\u30c9\u540d (httpRequest \u306a\u3069) \u3092\u4f7f\u7528\u3057\u307e\u3059\n# microsecond \u7cbe\u5ea6\u306e\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7\u3092\u542b\u3081\u3066\u3044\u307e\u3059\nLogFormat &quot;{\\\n  \\&quot;time\\&quot;:\\&quot;%{%Y-%m-%dT%H:%M:%S}t.%{usec_frac}tZ\\&quot;\\\n  , \\&quot;severity\\&quot;:\\&quot;INFO\\&quot;\\\n  , \\&quot;forwardedFor\\&quot;:\\&quot;%{X-Forwarded-For}i\\&quot;\\\n  , \\&quot;peerIp\\&quot;:\\&quot;%h\\&quot;\\\n  , \\&quot;httpRequest\\&quot;:{\\\n    \\&quot;requestMethod\\&quot;:\\&quot;%m\\&quot;\\\n    , \\&quot;requestUrl\\&quot;:\\&quot;https:\/\/%{Host}i%{REQUEST_URI}e\\&quot;\\\n    , \\&quot;requestSize\\&quot;:%I\\\n    , \\&quot;status\\&quot;:\\&quot;%&gt;s\\&quot;\\\n    , \\&quot;userAgent\\&quot;:\\&quot;%{User-Agent}i\\&quot;\\\n    , \\&quot;remoteIp\\&quot;:\\&quot;%{real_client_ip}e\\&quot;\\\n    , \\&quot;serverIp\\&quot;:\\&quot;%A\\&quot;\\\n    , \\&quot;referer\\&quot;:\\&quot;%{Referer}i\\&quot;\\\n    , \\&quot;responseSize\\&quot;:%B\\\n    , \\&quot;protocol\\&quot;:\\&quot;%H\\&quot;\\\n  }\\\n  , \\&quot;totalBytesSent\\&quot;:%O\\\n  , \\&quot;keepAliveCount\\&quot;:%k\\\n  , \\&quot;internalResource\\&quot;:\\&quot;%U\\&quot;\\\n  , \\&quot;serverLatencyMicros\\&quot;:%D\\\n  , \\&quot;logging.googleapis.com\/trace\\&quot;:\\&quot;projects\/%{PROJECT_ID}e\/traces\/%{google_trace_id}e\\&quot;\\\n  , \\&quot;logging.googleapis.com\/spanId\\&quot;:\\&quot;%{google_span_id}e\\&quot;\\\n}&quot; json_combined\n\n# \u5b9a\u7fa9\u3057\u305f json_combined \u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3092\u9069\u7528\nCustomLog ${APACHE_LOG_DIR}\/access.log json_combined<\/code><\/pre>\n<h3>\u91cd\u8981\u306a\u30dd\u30a4\u30f3\u30c8: <code>logging.googleapis.com\/trace<\/code><\/h3>\n<p>Cloud Logging \u3067\u30ed\u30b0\u3092\u30b0\u30eb\u30fc\u30d4\u30f3\u30b0\u3059\u308b\u305f\u3081\u306e\u30ad\u30e2\u3068\u306a\u308b\u306e\u304c <code>logging.googleapis.com\/trace<\/code> \u30d5\u30a3\u30fc\u30eb\u30c9\u3067\u3059\u3002\u3053\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\u306e\u5024\u306f <code>projects\/[PROJECT_ID]\/traces\/[TRACE_ID]<\/code> \u3068\u3044\u3046\u5f62\u5f0f\u3067\u3042\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059 <a href=\"https:\/\/docs.cloud.google.com\/logging\/docs\/structured-logging?hl=ja#structured_logging_special_fields\">Logging \u30a8\u30fc\u30b8\u30a7\u30f3\u30c8: \u7279\u5225\u306a JSON \u30d5\u30a3\u30fc\u30eb\u30c9<\/a>\u3002<\/p>\n<p>Apache \u306e\u8a2d\u5b9a\u5185\u3067 <code>%{PROJECT_ID}e<\/code> \u3068 <code>%{google_trace_id}e<\/code> \u3092\u4f7f\u3063\u3066\u52d5\u7684\u306b\u3053\u306e\u6587\u5b57\u5217\u3092\u57cb\u3081\u8fbc\u3093\u3067\u51fa\u529b\u3057\u3066\u3044\u307e\u3059\u3002\u3053\u308c\u306b\u3088\u308a\u3001Cloud Logging \u306e\u753b\u9762\u3067\u300c\u3053\u306e\u30c8\u30ec\u30fc\u30b9\u306e\u30a8\u30f3\u30c8\u30ea\u3092\u8868\u793a (Show entries for this trace)\u300d\u6a5f\u80fd\u304c\u4f7f\u3048\u308b\u3088\u3046\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n<h3>ErrorLog \u306e\u69cb\u9020\u5316<\/h3>\n<p>\u30a8\u30e9\u30fc\u30ed\u30b0\u306b\u3064\u3044\u3066\u3082\u540c\u69d8\u306b <code>ErrorLogFormat<\/code> \u3092\u4f7f\u3063\u3066 JSON \u5316\u3059\u308b\u3053\u3068\u304c\u53ef\u80fd\u3067\u3059\u3002ErrorLog \u3067\u306f\u3001\u672a\u5b9a\u7fa9\u5909\u6570\u304c\u3042\u308b\u5834\u5408\u306b\u3001\u300c\u305d\u306e\u56fa\u307e\u308a\u3054\u3068\u300d\u7701\u7565\u3055\u308c\u308b\u3068\u3044\u3046\u4ed5\u69d8\u304c\u3042\u308a\u307e\u3059\u3002\u4f8b\u3048\u3070\u3001\u8d77\u52d5\u6642\u306e\u30ed\u30b0\u306b\u306f Trace ID \u306f\u542b\u307e\u308c\u307e\u305b\u3093\u3002\u305d\u306e\u5834\u5408\u306b\u3001\u6ce8\u610f\u3057\u306a\u3044\u3068 JSON \u304c\u58ca\u308c\u3066\u3057\u307e\u3044\u307e\u3059\u3002<\/p>\n<p>\u4ee5\u4e0b\u306e\u8a2d\u5b9a\u4f8b\u3067\u306f\u3001<code>% ,<\/code> \u3068\u3044\u3046\u5f62\u5f0f\u3067\u30ab\u30f3\u30de\u3054\u3068\u7701\u7565\u3055\u308c\u308b\u3088\u3046\u306b\u3057\u3066\u3044\u307e\u3059\u3002\u300c\u30ab\u30f3\u30de\u306e\u5f8c\u306b\u30b9\u30da\u30fc\u30b9\u3092\u5165\u308c\u306a\u3044\u300d\u306e\u304c\u30dd\u30a4\u30f3\u30c8\u3067\u3059\u3002JSON \u306e\u69cb\u9020\u304c\u58ca\u308c\u306a\u3044\u3088\u3046\u306b\u5de5\u592b\u3057\u3066\u3044\u307e\u3059\u3002\u8a73\u3057\u304f\u306f <a href=\"https:\/\/httpd.apache.org\/docs\/2.4\/en\/mod\/core.html#errorlogformat\">ErrorLogFormat \u30c9\u30ad\u30e5\u30e1\u30f3\u30c8<\/a> \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u5148\u30ab\u30f3\u30de\u5f62\u5f0f\u3092\u4f7f\u3046\u3053\u3068\u3067\u3001Trace ID \u304c\u306a\u3044\u5834\u5408\u3067\u3082\u6709\u52b9\u306a JSON \u304c\u51fa\u529b\u3055\u308c\u307e\u3059\u3002<\/p>\n<p>\u5b9f\u306f\u3001ErrorLog \u306e\u65b9\u306f\u3001microsecond \u7cbe\u5ea6\u306e\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7\u3092\u51fa\u529b\u3067\u304d\u306a\u3044\u305f\u3081\u3001time \u30d5\u30a3\u30fc\u30eb\u30c9\u306f\u7701\u7565\u3057\u3066\u3044\u307e\u3059\u3002Cloud Run \u306e\u57fa\u76e4\u5074\u3067\u30ed\u30b0\u3092\u53d7\u3051\u53d6\u3063\u305f\u969b\u306b\u81ea\u52d5\u3067\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7\u304c\u4ed8\u4e0e\u3055\u308c\u308b\u305f\u3081\u3001\u554f\u984c\u3042\u308a\u307e\u305b\u3093\u3002<\/p>\n<pre><code class=\"language-conf:other-vhosts-access-log.conf\"># ErrorLog JSON format.\n# &quot;% &quot; \u3092\u4f7f\u3046\u3053\u3068\u3067\u3001\u5909\u6570\u304c\u672a\u5b9a\u7fa9\u306e\u5834\u5408\u306b\u30ab\u30f3\u30de\u3054\u3068\u7701\u7565\u3055\u308c\u308b\u4ed5\u69d8\u3067\u3059\u3002\n# \u53c2\u7167: https:\/\/httpd.apache.org\/docs\/2.4\/en\/mod\/core.html#errorlogformat\nErrorLogFormat &quot;{\\\n  \\&quot;severity\\&quot;:\\&quot;%l\\&quot;\\\n  % ,\\&quot;module\\&quot;:\\&quot;%m\\&quot;\\\n  % ,\\&quot;pid\\&quot;:\\&quot;%P\\&quot;\\\n  % ,\\&quot;tid\\&quot;:\\&quot;%T\\&quot;\\\n  % ,\\&quot;remoteIp\\&quot;:\\&quot;%a\\&quot;\\\n  % ,\\&quot;message\\&quot;:\\&quot;%M\\&quot;\\\n  % ,\\&quot;fileLocation\\&quot;:\\&quot;%F\\&quot;\\\n  % ,\\&quot;errorCode\\&quot;:\\&quot;%E\\&quot;\\\n  % ,\\&quot;logging.googleapis.com\/trace\\&quot;:\\&quot;projects\/%{PROJECT_ID}e\/traces\/%{google_trace_id}e\\&quot;\\\n  % ,\\&quot;logging.googleapis.com\/spanId\\&quot;:\\&quot;%{google_span_id}e\\&quot;\\\n  % \\\n}&quot;\nErrorLog ${APACHE_LOG_DIR}\/error.log<\/code><\/pre>\n<p>\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u305f\u969b\u3082 Trace ID \u304c\u8a18\u9332\u3055\u308c\u308b\u305f\u3081\u3001\u7279\u5b9a\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u3067\u4f55\u304c\u8d77\u304d\u305f\u304b\u3092\u8ffd\u8de1\u3057\u3084\u3059\u304f\u306a\u308a\u307e\u3059\u3002<\/p>\n<h2>Dockerfile \u3067\u306e\u9069\u7528\u4f8b<\/h2>\n<p>WordPress \u306e\u516c\u5f0f\u30a4\u30e1\u30fc\u30b8\u306a\u3069\u3092\u4f7f\u7528\u3057\u3066\u3044\u308b\u5834\u5408\u3001\u65e2\u5b58\u306e\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u3092\u4e0a\u66f8\u304d\u3057\u305f\u308a\u3001\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u8a2d\u5b9a\u3092\u66f8\u304d\u63db\u3048\u305f\u308a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u4ee5\u4e0b\u306f <code>wordpress:php8.4-apache<\/code> \u3092\u30d9\u30fc\u30b9\u306b\u3057\u305f <code>Dockerfile<\/code> \u306e\u4f8b\u3067\u3059\u3002<\/p>\n<pre><code class=\"language-dockerfile\">FROM wordpress:php8.4-apache\n\n# \u4e0a\u8a18\u306e\u8a2d\u5b9a\u5185\u5bb9\u3092\u4fdd\u5b58\u3057\u305f misc\/other-vhosts-access-log.conf \u3092\u30b3\u30f3\u30c6\u30ca\u5185\u306e\u8a2d\u5b9a\u30d1\u30b9\u306b\u30b3\u30d4\u30fc\n# \u65e2\u5b58\u306e other-vhosts-access-log.conf \u306f\u524a\u9664\u3057\u3066\u304b\u3089\u30b3\u30d4\u30fc\uff08\u5ff5\u306e\u305f\u3081\uff09\n# \u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30b5\u30a4\u30c8\u8a2d\u5b9a (000-default.conf) \u3067\u4f7f\u308f\u308c\u3066\u3044\u308b &#039;combined&#039; \u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3092 &#039;json_combined&#039; \u306b\u7f6e\u63db\nRUN rm -f \/etc\/apache2\/conf-enabled\/other-vhosts-access-log.conf &amp;&amp; \\\n    sed -i &#039;s\/combined\/json_combined\/g&#039; \/etc\/apache2\/sites-available\/000-default.conf\n\nCOPY misc\/other-vhosts-access-log.conf \/etc\/apache2\/conf-enabled\/other-vhosts-access-log.conf\n\n# \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30b3\u30fc\u30c9\u306e\u30c7\u30d7\u30ed\u30a4\u7b49\n# COPY --chown=www-data:www-data root_dir\/ \/var\/www\/html\/<\/code><\/pre>\n<p>\u3053\u306e\u4f8b\u3067\u306f\u3001<code>other-vhosts-access-log.conf<\/code> \u3092\u72ec\u81ea\u306e\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\uff08\u524d\u8ff0\u306e <code>LogFormat<\/code> \u5b9a\u7fa9\u306a\u3069\u304c\u66f8\u304b\u308c\u305f\u3082\u306e\uff09\u3067\u4e0a\u66f8\u304d\u3059\u308b\u3053\u3068\u3067\u3001\u8a2d\u5b9a\u3092\u8aad\u307f\u8fbc\u307e\u305b\u3066\u3044\u307e\u3059\u3002\u307e\u305f\u3001<code>sed<\/code> \u30b3\u30de\u30f3\u30c9\u3067\u30c7\u30d5\u30a9\u30eb\u30c8\u306e <code>VirtualHost<\/code> \u8a2d\u5b9a\u304c\u65b0\u3057\u3044 <code>json_combined<\/code> \u3092\u4f7f\u3046\u3088\u3046\u306b\u5909\u66f4\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n<h2>\u5b9f\u969b\u306e\u30ed\u30b0\u78ba\u8a8d<\/h2>\n<p>\u5b9f\u969b\u306b <code>\/throw_err.php<\/code> \u3068\u3044\u3046\u610f\u56f3\u7684\u306b\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u308b\u30b9\u30af\u30ea\u30d7\u30c8\u3092\u4f5c\u6210\u3057\u3001\u30a2\u30af\u30bb\u30b9\u3057\u3066\u78ba\u8a8d\u3057\u3066\u307f\u307e\u3059\u30022\u56de\u30a2\u30af\u30bb\u30b9\u3092\u884c\u3044\u307e\u3057\u305f\u3002<\/p>\n<p><a href=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2026\/01\/lb_log.png\"><img decoding=\"async\" src=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2026\/01\/lb_log-1024x196.png\" alt=\"\u30ed\u30b09\u884c\" \/><\/a><\/p>\n<p>\u3072\u3068\u3064\u76ee\u306e Cloud Run \u306e\u30ed\u30fc\u30c9\u30d0\u30e9\u30f3\u30b5\u306e\u30ed\u30b0\u306e\u2261\u306e\u3088\u3046\u306a\u30a2\u30a4\u30b3\u30f3\u3092\u30af\u30ea\u30c3\u30af\u3057\u3001\u300c\u3053\u306e\u30c8\u30ec\u30fc\u30b9\u306e\u30a8\u30f3\u30c8\u30ea\u3092\u8868\u793a (Show entries for this trace)\u300d\u3092\u9078\u629e\u3057\u307e\u3059\u3002<\/p>\n<p><a href=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2026\/01\/show_entries_for_this_trace.png\"><img decoding=\"async\" src=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2026\/01\/show_entries_for_this_trace.png\" alt=\"Show entries for this trace\" \/><\/a><\/p>\n<p><code>trace=&quot;projects\/[PROJECT_ID]\/traces\/449ca1aaf24cfa5a399a6e967394359a&quot;<\/code> \u3068\u3044\u3046\u30d5\u30a3\u30eb\u30bf\u304c\u52a0\u308f\u308a\u307e\u3059\u3002<\/p>\n<p><a href=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2026\/01\/filter.png\"><img decoding=\"async\" src=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2026\/01\/filter.png\" alt=\"filter\" \/><\/a><\/p>\n<p><a href=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2026\/01\/filtered_logs.png\"><img decoding=\"async\" src=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2026\/01\/filtered_logs-1024x360.png\" alt=\"filtered logs\" \/><\/a><\/p>\n<p>\u4e0a\u56f3\u306f\u3001\u30d5\u30a3\u30eb\u30bf\u9069\u7528\u5f8c\u306e\u30ed\u30b0\u4e00\u89a7\u3067\u3059\u3002\u30d5\u30a3\u30eb\u30bf\u306b\u3088\u3063\u3066\u3001\u95a2\u4fc2\u3059\u308b\u30ed\u30b0\u3060\u3051\u304c\u62bd\u51fa\u3055\u308c\u3001\u6642\u7cfb\u5217\u306b\u4e26\u3093\u3067\u8868\u793a\u3055\u308c\u3066\u3044\u307e\u3059\u3002Cloud Logging \u306e Trace ID \u3067\u30d5\u30a3\u30eb\u30bf\u3059\u308b\u3053\u3068\u3067\u3001\u4ee5\u4e0b\u306e\u4e00\u9023\u306e4\u3064\u306e\u30ed\u30b0\u304c\u4e00\u3064\u306e\u753b\u9762\u306b\u96c6\u7d04\u3055\u308c\u3066\u8868\u793a\u3055\u308c\u307e\u3059\u3002<\/p>\n<ol>\n<li><strong>Cloud Load Balancing<\/strong>: \u6700\u521d\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u306e\u53d7\u3051\u4ed8\u3051<\/li>\n<li><strong>Apache Access Log<\/strong>: \u30b3\u30f3\u30c6\u30ca\u306e\u524d\u6bb5\u3067\u306e\u30a2\u30af\u30bb\u30b9\u8a18\u9332<\/li>\n<li><strong>Application Error Log<\/strong>: PHP \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u5185\u3067\u306e\u30ed\u30b0\u51fa\u529b\uff08<code>error_log<\/code> \u95a2\u6570\u306a\u3069\uff09<\/li>\n<li><strong>Application Error Log<\/strong>: PHP \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u5185\u3067\u306e\u30a8\u30e9\u30fc\u767a\u751f\uff08<code>throw<\/code> \u3057\u3066\u7d42\u4e86\u3057\u305f\uff09<\/li>\n<\/ol>\n<p>\u300c\u3069\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u304c\u539f\u56e0\u3067\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u305f\u306e\u304b\uff1f\u300d\u3092\u7c21\u5358\u306b\u7279\u5b9a\u3067\u304d\u308b\u3088\u3046\u306b\u306a\u308b\u305f\u3081\u3001\u969c\u5bb3\u5bfe\u5fdc\u3084\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0\u304c\u975e\u5e38\u306b\u697d\u306b\u306a\u308a\u307e\u3059\u3002\u307e\u305f\u3001\u30ea\u30af\u30a8\u30b9\u30c8\u304c\u3069\u3053\u3092\u901a\u3063\u3066\u3001\u6700\u7d42\u7684\u306b\u3069\u306e\u3088\u3046\u306a\u30a8\u30e9\u30fc\u306b\u306a\u3063\u305f\u306e\u304b\u304c\u4e00\u76ee\u77ad\u7136\u3068\u306a\u308a\u3001\u30c7\u30d0\u30c3\u30b0\u52b9\u7387\u304c\u5927\u5e45\u306b\u5411\u4e0a\u3057\u307e\u3059\u3002<\/p>\n<h2>\u6ce8\u610f\u70b9<\/h2>\n<p><strong>\u74b0\u5883\u5909\u6570 PROJECT_ID<\/strong>:<br \/>\nCloud Run \u306e\u74b0\u5883\u5909\u6570\u3068\u3057\u3066 <code>PROJECT_ID<\/code> \u3092\u6e21\u3059\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002Terraform \u3084 <code>gcloud run deploy<\/code> \u306e\u5f15\u6570\u3067\u8a2d\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u30e1\u30bf\u30c7\u30fc\u30bf\u30b5\u30fc\u30d0\u30fc\u304b\u3089\u53d6\u5f97\u3059\u308b\u65b9\u6cd5\u3082\u3042\u308a\u307e\u3059\u304c\u3001Apache \u306e\u8d77\u52d5\u524d\u306b\u74b0\u5883\u5909\u6570\u306b\u30bb\u30c3\u30c8\u3055\u308c\u3066\u3044\u308b\u65b9\u304c\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u3067\u306e\u6271\u3044\u304c\u7c21\u5358\u3067\u3059\u3002<\/p>\n<p><strong>\u30a8\u30b9\u30b1\u30fc\u30d7\u51e6\u7406<\/strong>:<br \/>\n<code>LogFormat<\/code> \u5185\u3067 <code>&quot;<\/code> \uff08\u30c0\u30d6\u30eb\u30af\u30a9\u30fc\u30c8\uff09\u3092\u30d0\u30c3\u30af\u30b9\u30e9\u30c3\u30b7\u30e5\u3067\u30a8\u30b9\u30b1\u30fc\u30d7\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u624b\u52d5\u3067 JSON \u3092\u7d44\u307f\u7acb\u3066\u3066\u3044\u308b\u305f\u3081\u3001User-Agent \u306a\u3069\u306b\u6539\u884c\u3084\u4e0d\u6b63\u306a\u6587\u5b57\u5217\u304c\u542b\u307e\u308c\u308b\u3068 JSON \u304c\u58ca\u308c\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u304c\u3001Apache \u306e\u30ed\u30b0\u51fa\u529b\u306f\u30c7\u30d5\u30a9\u30eb\u30c8\u3067\u5236\u5fa1\u6587\u5b57\u3092\u30a8\u30b9\u30b1\u30fc\u30d7\u3057\u3066\u304f\u308c\u308b\u305f\u3081\u3001\u591a\u304f\u306e\u5834\u5408\u554f\u984c\u306b\u306a\u308a\u307e\u305b\u3093\u3002<a href=\"https:\/\/httpd.apache.org\/docs\/current\/en\/mod\/mod_log_config.html#formats\">Format Notes<\/a> \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002<\/p>\n<h2>\u307e\u3068\u3081<\/h2>\n<p>Apache \u306e\u8a2d\u5b9a\u3092\u5c11\u3057\u5de5\u592b\u3059\u308b\u3060\u3051\u3067\u3001\u30e2\u30c0\u30f3\u306a\u30af\u30e9\u30a6\u30c9\u30cd\u30a4\u30c6\u30a3\u30d6\u74b0\u5883\u306b\u9069\u3057\u305f\u69cb\u9020\u5316\u30ed\u30b0\u3092\u51fa\u529b\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n<ol>\n<li><code>SetEnvIf<\/code> \u3067 <code>traceparent<\/code> \u304b\u3089 Trace ID \u3068 Span ID \u3092\u62bd\u51fa<\/li>\n<li><code>LogFormat<\/code> \u3067 JSON \u3092\u69cb\u7bc9\u3057\u3001<code>logging.googleapis.com\/trace<\/code> \u3092\u542b\u3081\u308b<\/li>\n<\/ol>\n<p>\u3053\u308c\u306b\u3088\u308a\u3001Cloud Logging \u4e0a\u3067\u300c\u30b9\u30c6\u30fc\u30bf\u30b9\u30b3\u30fc\u30c9 500 \u306e\u30ea\u30af\u30a8\u30b9\u30c8\u300d\u3092\u30d5\u30a3\u30eb\u30bf\u30ea\u30f3\u30b0\u3057\u3001\u305d\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u306b\u95a2\u9023\u3059\u308b\u300cApache \u306e\u30a2\u30af\u30bb\u30b9\u30ed\u30b0\u300d\u3068\u300c\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30a8\u30e9\u30fc\u30ed\u30b0\u300d\u3092 Trace ID \u3067\u7d10\u4ed8\u3051\u3066\u4e00\u6c17\u901a\u8cab\u3067\u78ba\u8a8d\u3067\u304d\u308b\u3088\u3046\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n<p>\u53ef\u89b3\u6e2c\u6027\u304c\u5411\u4e0a\u3059\u308b\u3068\u3001\u969c\u5bb3\u5bfe\u5fdc\u3084\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0\u306e\u52b9\u7387\u304c\u683c\u6bb5\u306b\u4e0a\u304c\u308a\u307e\u3059\u3002\u305c\u3072\u8a66\u3057\u3066\u307f\u3066\u304f\u3060\u3055\u3044\u3002<\/p>\n<hr \/>\n<h1>Implementing Structured Logging (JSON) and Trace ID Integration for Apache on Cloud Run<\/h1>\n<h2>Overview<\/h2>\n<ul>\n<li>Output logs in JSON format (structured logging) when running Apache on Cloud Run or GKE.<\/li>\n<li>Include Trace IDs and Span IDs in logs to centralize and group requests in Cloud Logging.<\/li>\n<li>Handle Web server logs in the same way as application (e.g., PHP) logs.<\/li>\n<li>Achieve this using only Apache configuration files, without additional modules or sidecars.<\/li>\n<\/ul>\n<h2>Introduction<\/h2>\n<p>When operating web applications in container environments like Cloud Run, log observability is crucial. <a href=\"https:\/\/tako.nakano.net\/blog\/2025\/05\/structured-logging-in-rails-8\/\">In a previous article<\/a>, we introduced a request to structure logs for Ruby on Rails applications and integrate Trace IDs.<\/p>\n<p>This time, we focus on <strong>Apache HTTP Server<\/strong>, which often operates as a frontend for WordPress or PHP applications. We will introduce a method to make log investigation on Cloud Logging dramatically more comfortable by outputting Apache access logs and error logs in JSON format and embedding Google Cloud Trace IDs and Span IDs.<\/p>\n<p>A similar approach is possible if you are using Nginx. You can use directives like <code>log_format json escape=json<\/code>, which makes configuration simpler than Apache. This article focuses specifically on Apache.<\/p>\n<p>Also, a similar approach is possible if you are running Apache behind Google Cloud Load Balancing (GCLB) (for example, on GKE) instead of Cloud Run. This article assumes Cloud Run.<\/p>\n<p>We also use similar settings in the Zero Scale WordPress architecture I proposed, which zero-scales the database for WordPress. For reference, please see the example configuration files in the following repository:<br \/>\n<a href=\"https:\/\/github.com\/takotakot\/zeroscale-wp\/blob\/main\/misc\/other-vhosts-access-log.conf\">https:\/\/github.com\/takotakot\/zeroscale-wp\/blob\/main\/misc\/other-vhosts-access-log.conf<\/a> and <a href=\"https:\/\/github.com\/takotakot\/zeroscale-wp\/blob\/main\/Dockerfile\">https:\/\/github.com\/takotakot\/zeroscale-wp\/blob\/main\/Dockerfile<\/a><\/p>\n<h2>Background and Challenges<\/h2>\n<p>The default Apache log format (Combined Log Format) is plain text, which is treated as <code>textPayload<\/code> when sent to Cloud Logging. This presents two inconveniences:<\/p>\n<ol>\n<li><strong>Difficulty in Querying<\/strong>: Filtering by status code or response time requires using regular expressions, which is cumbersome.<\/li>\n<li><strong>Fragmentation of Traces<\/strong>: Cloud Run assigns a Trace ID to each request, but this is not included in Apache&#8217;s standard logs. This makes it difficult to display logs related to a specific request (Apache access logs + application logs) together.<\/li>\n<\/ol>\n<p>To solve these issues, we will tweak the Apache configuration files to output JSON logs suitable for Google Cloud Logging.<\/p>\n<h2>Implementation Approach<\/h2>\n<p>There are two key points to the implementation:<\/p>\n<ol>\n<li><strong>Extract Trace ID and Span ID<\/strong>: Extract the Trace ID and Span ID from the request header (<code>traceparent<\/code> or <code>X-Cloud-Trace-Context<\/code>) and set them to environment variables.<\/li>\n<li><strong>Define JSON Log Format<\/strong>: Use the <code>LogFormat<\/code> directive to manually construct a JSON string.<\/li>\n<\/ol>\n<h3>Configuration File Implementation<\/h3>\n<p>Add the following settings to your base configuration file (e.g., <code>other-vhosts-access-log.conf<\/code> or a site configuration file).<\/p>\n<p>In this configuration, we use the <code>mod_setenvif<\/code> module to extract the Trace ID \/ Span ID and store it in the <code>google_trace_id<\/code>\/<code>google_span_id<\/code> environment variable. It is better to pass <code>PROJECT_ID<\/code> as an environment variable rather than specifying it directly. This eliminates the need to separate container images for each environment. Since access logs record requests, the Severity is set to INFO.<\/p>\n<pre><code class=\"language-conf:other-vhosts-access-log.conf\"># JSON log format configuration\n# Allow breaking when User-Agent or Referer contains newline or double quotes.\n\n# PROJECT_ID environment variable is required on Cloud Run\nPassEnv PROJECT_ID\n\n# Trace ID Extraction\n# Extraction from legacy X-Cloud-Trace-Context (just in case)\nSetEnvIf X-Cloud-Trace-Context &quot;^([0-9a-f]{32})\/([0-9a-f]+).*$&quot; google_trace_id=$1 google_span_id=$2\n# Extraction from W3C Trace Context (traceparent) (This is preferred)\nSetEnvIf traceparent &quot;^00-([a-f0-9]{32})-([a-f0-9]{16})-.+$&quot; google_trace_id=$1 google_span_id=$2\n\n# Setting other convenient variables\nSetEnvIf Remote_Addr &quot;^(.*)$&quot; real_client_ip=$1\nSetEnvIf X-Forwarded-For &quot;^([^,]+)&quot; real_client_ip=$1\nSetEnvIf Request_URI &quot;^(.*)$&quot; REQUEST_URI=$1\n\n# AccessLog JSON format\n# Use field names that Cloud Logging can interpret (like httpRequest)\n# Include timestamp with microsecond precision\nLogFormat &quot;{\\\n  \\&quot;time\\&quot;:\\&quot;%{%Y-%m-%dT%H:%M:%S}t.%{usec_frac}tZ\\&quot;\\\n  , \\&quot;severity\\&quot;:\\&quot;INFO\\&quot;\\\n  , \\&quot;forwardedFor\\&quot;:\\&quot;%{X-Forwarded-For}i\\&quot;\\\n  , \\&quot;peerIp\\&quot;:\\&quot;%h\\&quot;\\\n  , \\&quot;httpRequest\\&quot;:{\\\n    \\&quot;requestMethod\\&quot;:\\&quot;%m\\&quot;\\\n    , \\&quot;requestUrl\\&quot;:\\&quot;https:\/\/%{Host}i%{REQUEST_URI}e\\&quot;\\\n    , \\&quot;requestSize\\&quot;:%I\\\n    , \\&quot;status\\&quot;:\\&quot;%&gt;s\\&quot;\\\n    , \\&quot;userAgent\\&quot;:\\&quot;%{User-Agent}i\\&quot;\\\n    , \\&quot;remoteIp\\&quot;:\\&quot;%{real_client_ip}e\\&quot;\\\n    , \\&quot;serverIp\\&quot;:\\&quot;%A\\&quot;\\\n    , \\&quot;referer\\&quot;:\\&quot;%{Referer}i\\&quot;\\\n    , \\&quot;responseSize\\&quot;:%B\\\n    , \\&quot;protocol\\&quot;:\\&quot;%H\\&quot;\\\n  }\\\n  , \\&quot;totalBytesSent\\&quot;:%O\\\n  , \\&quot;keepAliveCount\\&quot;:%k\\\n  , \\&quot;internalResource\\&quot;:\\&quot;%U\\&quot;\\\n  , \\&quot;serverLatencyMicros\\&quot;:%D\\\n  , \\&quot;logging.googleapis.com\/trace\\&quot;:\\&quot;projects\/%{PROJECT_ID}e\/traces\/%{google_trace_id}e\\&quot;\\\n  , \\&quot;logging.googleapis.com\/spanId\\&quot;:\\&quot;%{google_span_id}e\\&quot;\\\n}&quot; json_combined\n\n# Apply the defined json_combined format\nCustomLog ${APACHE_LOG_DIR}\/access.log json_combined<\/code><\/pre>\n<h3>Important Point: <code>logging.googleapis.com\/trace<\/code><\/h3>\n<p>The key to grouping logs in Cloud Logging is the <code>logging.googleapis.com\/trace<\/code> field. The value of this field must be in the format <code>projects\/[PROJECT_ID]\/traces\/[TRACE_ID]<\/code> <a href=\"https:\/\/cloud.google.com\/logging\/docs\/structured-logging#structured_logging_special_fields\">Logging agent: Special JSON fields<\/a>.<\/p>\n<p>In the Apache configuration, we dynamically embed this string using <code>%{PROJECT_ID}e<\/code> and <code>%{google_trace_id}e<\/code> for output. This enables the &quot;Show entries for this trace&quot; feature in the Cloud Logging interface.<\/p>\n<h3>Structuring ErrorLog<\/h3>\n<p>Similarly, <code>ErrorLog<\/code> can be converted to JSON using <code>ErrorLogFormat<\/code>. For <code>ErrorLog<\/code>, there is a specification where if a variable is undefined, the &quot;chunk&quot; containing it is omitted. For example, startup logs do not contain a Trace ID. In such cases, care must be taken not to break the JSON.<\/p>\n<p>In the configuration example below, we use the format <code>% ,<\/code> to omit the comma along with the variable. The point is &quot;do not put a space after the comma&quot;. This is a trick to prevent the JSON structure from breaking. For details, refer to the <a href=\"https:\/\/httpd.apache.org\/docs\/2.4\/en\/mod\/core.html#errorlogformat\">ErrorLogFormat documentation<\/a>. Using the leading comma format outputs valid JSON even when the Trace ID is missing.<\/p>\n<p>Actually, the <code>ErrorLog<\/code> cannot output timestamps with microsecond precision, so the <code>time<\/code> field is omitted. Since the Cloud Run infrastructure automatically adds a timestamp when it receives the log, this is not an issue.<\/p>\n<pre><code class=\"language-conf:other-vhosts-access-log.conf\"># ErrorLog JSON format.\n# By using &quot;% &quot;, the specification allows omitting the entire chunk including the comma when the variable is undefined.\n# Reference: https:\/\/httpd.apache.org\/docs\/2.4\/en\/mod\/core.html#errorlogformat\nErrorLogFormat &quot;{\\\n  \\&quot;severity\\&quot;:\\&quot;%l\\&quot;\\\n  % ,\\&quot;module\\&quot;:\\&quot;%m\\&quot;\\\n  % ,\\&quot;pid\\&quot;:\\&quot;%P\\&quot;\\\n  % ,\\&quot;tid\\&quot;:\\&quot;%T\\&quot;\\\n  % ,\\&quot;remoteIp\\&quot;:\\&quot;%a\\&quot;\\\n  % ,\\&quot;message\\&quot;:\\&quot;%M\\&quot;\\\n  % ,\\&quot;fileLocation\\&quot;:\\&quot;%F\\&quot;\\\n  % ,\\&quot;errorCode\\&quot;:\\&quot;%E\\&quot;\\\n  % ,\\&quot;logging.googleapis.com\/trace\\&quot;:\\&quot;projects\/%{PROJECT_ID}e\/traces\/%{google_trace_id}e\\&quot;\\\n  % ,\\&quot;logging.googleapis.com\/spanId\\&quot;:\\&quot;%{google_span_id}e\\&quot;\\\n  % \\\n}&quot;\nErrorLog ${APACHE_LOG_DIR}\/error.log<\/code><\/pre>\n<p>Trace IDs are recorded even when errors occur, making it easier to track what happened with a specific request.<\/p>\n<h2>Dockerfile Application Example<\/h2>\n<p>If you are using an official image like WordPress, you may need to overwrite existing configuration files or rewrite default settings. Below is an example of a <code>Dockerfile<\/code> based on <code>wordpress:php8.4-apache<\/code>.<\/p>\n<pre><code class=\"language-dockerfile\">FROM wordpress:php8.4-apache\n\n# Copy misc\/other-vhosts-access-log.conf containing the above settings to the configuration path in the container\n# Delete the existing other-vhosts-access-log.conf before copying (just in case)\n# Replace the &#039;combined&#039; format used in the default site configuration (000-default.conf) with &#039;json_combined&#039;\nRUN rm -f \/etc\/apache2\/conf-enabled\/other-vhosts-access-log.conf &amp;&amp; \\\n    sed -i &#039;s\/combined\/json_combined\/g&#039; \/etc\/apache2\/sites-available\/000-default.conf\n\nCOPY misc\/other-vhosts-access-log.conf \/etc\/apache2\/conf-enabled\/other-vhosts-access-log.conf\n\n# Deploy application code, etc.\n# COPY --chown=www-data:www-data root_dir\/ \/var\/www\/html\/<\/code><\/pre>\n<p>In this example, we overwrite <code>other-vhosts-access-log.conf<\/code> with our own configuration file (containing the aforementioned <code>LogFormat<\/code> definition, etc.) to load the settings. Also, we use the <code>sed<\/code> command to change the default <code>VirtualHost<\/code> configuration to use the new <code>json_combined<\/code>.<\/p>\n<h2>Actual Log Verification<\/h2>\n<p>Let&#8217;s verify this by creating a script called <code>\/throw_err.php<\/code> that intentionally generates an error and accessing it. I accessed it twice.<\/p>\n<p><a href=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2026\/01\/lb_log.png\"><img decoding=\"async\" src=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2026\/01\/lb_log.png\" alt=\"9 lines of logs\" \/><\/a><\/p>\n<p>Click on the icon that looks like \u2261 in the first Cloud Run load balancer log and select &quot;Show entries for this trace&quot;.<\/p>\n<p><a href=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2026\/01\/show_entries_for_this_trace.png\"><img decoding=\"async\" src=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2026\/01\/show_entries_for_this_trace.png\" alt=\"Show entries for this trace\" \/><\/a><\/p>\n<p>A filter like <code>trace=&quot;projects\/[PROJECT_ID]\/traces\/449ca1aaf24cfa5a399a6e967394359a&quot;<\/code> is added.<\/p>\n<p><a href=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2026\/01\/filter.png\"><img decoding=\"async\" src=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2026\/01\/filter.png\" alt=\"filter\" \/><\/a><\/p>\n<p><a href=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2026\/01\/filtered_logs.png\"><img decoding=\"async\" src=\"https:\/\/tako.nakano.net\/blog\/wp-content\/uploads\/2026\/01\/filtered_logs.png\" alt=\"filtered logs\" \/><\/a><\/p>\n<p>The figure above shows the list of logs after applying the filter. As you can see, by filtering with the Cloud Logging Trace ID, the following series of four logs are aggregated and displayed on a single screen.<\/p>\n<ol>\n<li><strong>Cloud Load Balancing<\/strong>: Receipt of the initial request<\/li>\n<li><strong>Apache Access Log<\/strong>: Access record at the container frontend<\/li>\n<li><strong>Application Error Log<\/strong>: Log output within the PHP application (e.g., <code>error_log<\/code> function)<\/li>\n<li><strong>Application Error Log<\/strong>: Error occurrence within the PHP application (terminated by <code>throw<\/code>)<\/li>\n<\/ol>\n<p>This makes it very easy to identify &quot;which request caused the error&quot;, greatly easing troubleshooting and performance tuning. Also, the path of the request and the final error become obvious at a glance, significantly improving debugging efficiency.<\/p>\n<h2>Important Notes<\/h2>\n<p><strong>PROJECT_ID Environment Variable<\/strong>:<br \/>\nYou need to pass <code>PROJECT_ID<\/code> as a Cloud Run environment variable. Set it via Terraform or the arguments for <code>gcloud run deploy<\/code>. While it&#8217;s possible to retrieve it from the metadata server, it is easier to handle in the configuration file if it is set in an environment variable before Apache starts.<\/p>\n<p><strong>Escaping<\/strong>:<br \/>\nYou need to escape <code>&quot;<\/code> (double quotes) with a backslash inside <code>LogFormat<\/code>. Since we are manually assembling JSON, the JSON might break if User-Agent or other fields contain newlines or invalid strings. However, Apache&#8217;s log output escapes control characters by default, so this is rarely a problem. Please refer to <a href=\"https:\/\/httpd.apache.org\/docs\/current\/en\/mod\/mod_log_config.html#formats\">Format Notes<\/a>.<\/p>\n<h2>Summary<\/h2>\n<p>With just a few tweaks to your Apache configuration, you can output structured logs suitable for modern cloud-native environments.<\/p>\n<ol>\n<li>Extract Trace ID and Span ID from <code>traceparent<\/code> using <code>SetEnvIf<\/code>.<\/li>\n<li>Construct JSON using <code>LogFormat<\/code> and include <code>logging.googleapis.com\/trace<\/code>.<\/li>\n<\/ol>\n<p>This allows you to filter &quot;requests with status code 500&quot; on Cloud Logging and check the related &quot;Apache access logs&quot; and &quot;Application error logs&quot; linked by Trace ID in one go.<\/p>\n<p>Improved observability significantly increases the efficiency of troubleshooting and performance tuning. Please give it a try.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Cloud Run \u4e0a\u306e Apache \u3067\u69cb\u9020\u5316\u30ed\u30b0 (JSON) \u3068 Trace ID \u9023\u643a\u3092\u5b9f\u73fe\u3059\u308b English follows Japanese. \u6982\u8981 Cloud Run \u3084 GKE \u3067 Apache \u3092\u52d5\u304b [&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":[29,26,13],"tags":[],"class_list":["post-637","post","type-post","status-publish","format-standard","hentry","category-cloud-logging","category-cloud-run","category-google-cloud"],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p4dIdP-ah","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/posts\/637","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=637"}],"version-history":[{"count":3,"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/posts\/637\/revisions"}],"predecessor-version":[{"id":644,"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/posts\/637\/revisions\/644"}],"wp:attachment":[{"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/media?parent=637"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/categories?post=637"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/tags?post=637"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}