From 875524a38cb0a0b2cfaa2b5b058a8714f8900b45 Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Wed, 1 Jan 2025 14:24:42 -0800 Subject: [PATCH 1/7] adding a replacement for the default route logging middleware from Vapor that adds in the cf-ray id, if any, from cloudflare --- .../App/Core/CFRayRouteLoggingMiddleware.swift | 17 +++++++++++++++++ Sources/App/configure.swift | 9 +++++++++ 2 files changed, 26 insertions(+) create mode 100644 Sources/App/Core/CFRayRouteLoggingMiddleware.swift diff --git a/Sources/App/Core/CFRayRouteLoggingMiddleware.swift b/Sources/App/Core/CFRayRouteLoggingMiddleware.swift new file mode 100644 index 000000000..475d0c6f0 --- /dev/null +++ b/Sources/App/Core/CFRayRouteLoggingMiddleware.swift @@ -0,0 +1,17 @@ +import Vapor + +// Replica of the Vapor RouteLoggingMiddleware that's tweaked to explicitly expose the cf-ray header in the logger metadata for the request. +public final class CFRayRouteLoggingMiddleware: Middleware { + public let logLevel: Logger.Level + + public init(logLevel: Logger.Level = .info) { + self.logLevel = logLevel + } + + public func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture { + let cfray = request.headers.first(name: "cf-ray") ?? "--nil--" + request.logger[metadataKey: "cf-ray"] = .string(cfray) + request.logger.log(level: self.logLevel, "\(request.method) \(request.url.path.removingPercentEncoding ?? request.url.path)") + return next.respond(to: request) + } +} diff --git a/Sources/App/configure.swift b/Sources/App/configure.swift index c8a65988a..992a85a07 100644 --- a/Sources/App/configure.swift +++ b/Sources/App/configure.swift @@ -37,6 +37,15 @@ public func configure(_ app: Application) async throws -> String { // app.http.server.configuration.responseCompression = .enabled // app.http.server.configuration.requestDecompression = .enabled + // This stanza replaces the default middleware from Vapor, specifically replacing the + // default route logging middleware with the tweaked up version that reports on the cf-ray + // header (if any) from CloudFlare. + app.middleware = Middlewares() + app.middleware.use(Vapor.ErrorMiddleware.default(environment: app.environment)) + app.middleware.use(CFRayRouteLoggingMiddleware()) + // disable the 3 lines above to return to the default middleware setup before we add SPI-specific + // middleware. + app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory)) app.middleware.use(ErrorMiddleware()) From 29cb4a3d33c039c0fc22805958ec44f4ba219668 Mon Sep 17 00:00:00 2001 From: Joseph Heck Date: Thu, 2 Jan 2025 04:34:52 -0800 Subject: [PATCH 2/7] Update Sources/App/Core/CFRayRouteLoggingMiddleware.swift Co-authored-by: Sven A. Schmidt --- Sources/App/Core/CFRayRouteLoggingMiddleware.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/App/Core/CFRayRouteLoggingMiddleware.swift b/Sources/App/Core/CFRayRouteLoggingMiddleware.swift index 475d0c6f0..7b9bbfd62 100644 --- a/Sources/App/Core/CFRayRouteLoggingMiddleware.swift +++ b/Sources/App/Core/CFRayRouteLoggingMiddleware.swift @@ -9,7 +9,9 @@ public final class CFRayRouteLoggingMiddleware: Middleware { } public func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture { - let cfray = request.headers.first(name: "cf-ray") ?? "--nil--" + guard let cfray = request.headers.first(name: "cf-ray") else { + return next.respond(to: request) + } request.logger[metadataKey: "cf-ray"] = .string(cfray) request.logger.log(level: self.logLevel, "\(request.method) \(request.url.path.removingPercentEncoding ?? request.url.path)") return next.respond(to: request) From d4cc7c0e452c589fd9638e75bf6acfd7cb9c8bf5 Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Thu, 2 Jan 2025 10:57:05 +0100 Subject: [PATCH 3/7] Add copyright preamble --- Sources/App/Core/CFRayRouteLoggingMiddleware.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Sources/App/Core/CFRayRouteLoggingMiddleware.swift b/Sources/App/Core/CFRayRouteLoggingMiddleware.swift index 7b9bbfd62..0081db048 100644 --- a/Sources/App/Core/CFRayRouteLoggingMiddleware.swift +++ b/Sources/App/Core/CFRayRouteLoggingMiddleware.swift @@ -1,3 +1,17 @@ +// Copyright Dave Verwer, Sven A. Schmidt, and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import Vapor // Replica of the Vapor RouteLoggingMiddleware that's tweaked to explicitly expose the cf-ray header in the logger metadata for the request. From 0e21e2d0f503d2cdcc7526db320af6fe10e8fe21 Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Thu, 2 Jan 2025 11:00:55 +0100 Subject: [PATCH 4/7] Add EnvironmentClient.enableCFRayLogging --- Sources/App/Core/Dependencies/EnvironmentClient.swift | 6 ++++++ app.yml | 1 + 2 files changed, 7 insertions(+) diff --git a/Sources/App/Core/Dependencies/EnvironmentClient.swift b/Sources/App/Core/Dependencies/EnvironmentClient.swift index 836473183..3ab254de9 100644 --- a/Sources/App/Core/Dependencies/EnvironmentClient.swift +++ b/Sources/App/Core/Dependencies/EnvironmentClient.swift @@ -44,6 +44,7 @@ struct EnvironmentClient { var hideStagingBanner: @Sendable () -> Bool = { XCTFail("hideStagingBanner"); return Constants.defaultHideStagingBanner } var loadSPIManifest: @Sendable (String) -> SPIManifest.Manifest? var maintenanceMessage: @Sendable () -> String? + var enableCFRayLogging: @Sendable () -> Bool = { XCTFail("enableCFRayLogging"); return true } var mastodonCredentials: @Sendable () -> Mastodon.Credentials? var metricsPushGatewayUrl: @Sendable () -> String? var plausibleBackendReportingSiteID: @Sendable () -> String? @@ -116,6 +117,10 @@ extension EnvironmentClient: DependencyKey { loadSPIManifest: { path in SPIManifest.Manifest.load(in: path) }, maintenanceMessage: { Environment.get("MAINTENANCE_MESSAGE").flatMap(\.trimmed) + enableCFRayLogging: { + Environment.get("ENABLE_CF_RAY_LOGGING") + .flatMap(\.asBool) + ?? false }, mastodonCredentials: { Environment.get("MASTODON_ACCESS_TOKEN") @@ -172,6 +177,7 @@ extension EnvironmentClient: TestDependencyKey { mock.hideStagingBanner = { false } mock.siteURL = { "http://localhost:8080" } mock.shouldFail = { @Sendable _ in false } + mock.enableCFRayLogging = { true } return mock } } diff --git a/app.yml b/app.yml index a0b515fbf..c34f9ab63 100644 --- a/app.yml +++ b/app.yml @@ -44,6 +44,7 @@ x-shared: &shared DATABASE_USERNAME: ${DATABASE_USERNAME} DATABASE_PASSWORD: ${DATABASE_PASSWORD} DATABASE_USE_TLS: ${DATABASE_USE_TLS} + ENABLE_CF_RAY_LOGGING: ${ENABLE_CF_RAY_LOGGING} FAILURE_MODE: ${FAILURE_MODE} GITHUB_TOKEN: ${GITHUB_TOKEN} GITLAB_API_TOKEN: ${GITLAB_API_TOKEN} From 7b6d6029a3635f93777ba5561622868dba30bd3e Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Thu, 2 Jan 2025 16:11:48 +0100 Subject: [PATCH 5/7] Toggle logging according to env variable --- Sources/App/Core/CFRayRouteLoggingMiddleware.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Sources/App/Core/CFRayRouteLoggingMiddleware.swift b/Sources/App/Core/CFRayRouteLoggingMiddleware.swift index 0081db048..a60db2d19 100644 --- a/Sources/App/Core/CFRayRouteLoggingMiddleware.swift +++ b/Sources/App/Core/CFRayRouteLoggingMiddleware.swift @@ -12,20 +12,28 @@ // See the License for the specific language governing permissions and // limitations under the License. +import Dependencies import Vapor + // Replica of the Vapor RouteLoggingMiddleware that's tweaked to explicitly expose the cf-ray header in the logger metadata for the request. public final class CFRayRouteLoggingMiddleware: Middleware { public let logLevel: Logger.Level - + public init(logLevel: Logger.Level = .info) { self.logLevel = logLevel } - + public func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture { + @Dependency(\.environment) var environment + + guard environment.enableCFRayLogging() else { + return next.respond(to: request) + } guard let cfray = request.headers.first(name: "cf-ray") else { return next.respond(to: request) } + request.logger[metadataKey: "cf-ray"] = .string(cfray) request.logger.log(level: self.logLevel, "\(request.method) \(request.url.path.removingPercentEncoding ?? request.url.path)") return next.respond(to: request) From 17a735587007387e576046b6cdfe73423b2f98f2 Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Fri, 3 Jan 2025 18:04:23 +0100 Subject: [PATCH 6/7] Move the environment.enableCFRayLogging() check into configure, to fully restore original behaviour when off --- .../Core/CFRayRouteLoggingMiddleware.swift | 5 ----- Sources/App/configure.swift | 19 +++++++++++-------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Sources/App/Core/CFRayRouteLoggingMiddleware.swift b/Sources/App/Core/CFRayRouteLoggingMiddleware.swift index a60db2d19..e15ae0dab 100644 --- a/Sources/App/Core/CFRayRouteLoggingMiddleware.swift +++ b/Sources/App/Core/CFRayRouteLoggingMiddleware.swift @@ -25,11 +25,6 @@ public final class CFRayRouteLoggingMiddleware: Middleware { } public func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture { - @Dependency(\.environment) var environment - - guard environment.enableCFRayLogging() else { - return next.respond(to: request) - } guard let cfray = request.headers.first(name: "cf-ray") else { return next.respond(to: request) } diff --git a/Sources/App/configure.swift b/Sources/App/configure.swift index 992a85a07..f48f450bd 100644 --- a/Sources/App/configure.swift +++ b/Sources/App/configure.swift @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +import Dependencies import Fluent import FluentPostgresDriver import Vapor @@ -37,14 +38,16 @@ public func configure(_ app: Application) async throws -> String { // app.http.server.configuration.responseCompression = .enabled // app.http.server.configuration.requestDecompression = .enabled - // This stanza replaces the default middleware from Vapor, specifically replacing the - // default route logging middleware with the tweaked up version that reports on the cf-ray - // header (if any) from CloudFlare. - app.middleware = Middlewares() - app.middleware.use(Vapor.ErrorMiddleware.default(environment: app.environment)) - app.middleware.use(CFRayRouteLoggingMiddleware()) - // disable the 3 lines above to return to the default middleware setup before we add SPI-specific - // middleware. + @Dependency(\.environment) var environment + + if environment.enableCFRayLogging() { + // This stanza replaces the default middleware from Vapor, specifically replacing the + // default route logging middleware with the tweaked up version that reports on the cf-ray + // header (if any) from CloudFlare. + app.middleware = Middlewares() + app.middleware.use(Vapor.ErrorMiddleware.default(environment: app.environment)) + app.middleware.use(CFRayRouteLoggingMiddleware()) + } app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory)) app.middleware.use(ErrorMiddleware()) From 42784259862e09df6509d8f7d86cc0731d114207 Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Fri, 3 Jan 2025 18:07:23 +0100 Subject: [PATCH 7/7] Drop import --- Sources/App/Core/CFRayRouteLoggingMiddleware.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Sources/App/Core/CFRayRouteLoggingMiddleware.swift b/Sources/App/Core/CFRayRouteLoggingMiddleware.swift index e15ae0dab..ca5d5449e 100644 --- a/Sources/App/Core/CFRayRouteLoggingMiddleware.swift +++ b/Sources/App/Core/CFRayRouteLoggingMiddleware.swift @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -import Dependencies import Vapor @@ -23,12 +22,11 @@ public final class CFRayRouteLoggingMiddleware: Middleware { public init(logLevel: Logger.Level = .info) { self.logLevel = logLevel } - + public func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture { guard let cfray = request.headers.first(name: "cf-ray") else { return next.respond(to: request) } - request.logger[metadataKey: "cf-ray"] = .string(cfray) request.logger.log(level: self.logLevel, "\(request.method) \(request.url.path.removingPercentEncoding ?? request.url.path)") return next.respond(to: request)