Repository: vapor/vapor
File: Sources/Vapor/Middleware/ErrorMiddleware.swift (Swift)

Code before:
    public static func `default`(environment: Environment) -> ErrorMiddleware {
        return .init { req, error in
            let status: HTTPResponseStatus, reason: String, source: ErrorSource
            var headers: HTTPHeaders

            // Inspect the error type and extract what data we can.
            switch error {
            case let debugAbort as (DebuggableError & AbortError):
                (reason, status, headers, source) = (debugAbort.reason, debugAbort.status, debugAbort.headers, debugAbort.source ?? .capture())
                
            case let abort as AbortError:
                (reason, status, headers, source) = (abort.reason, abort.status, abort.headers, .capture())
            
            case let debugErr as DebuggableError:
                (reason, status, headers, source) = (debugErr.reason, .internalServerError, [:], debugErr.source ?? .capture())
            
            default:
                // In debug mode, provide the error description; otherwise hide it to avoid sensitive data disclosure.
                reason = environment.isRelease ? "Something went wrong." : String(describing: error)
                (status, headers, source) = (.internalServerError, [:], .capture())
            }
            
            // Report the error
            req.logger.report(error: error,
                              metadata: ["method" : .string(req.method.string),
                                         "url" : .string(req.url.string)],
                              file: source.file,
                              function: source.function,
                              line: source.line)
            
            // attempt to serialize the error to json
            let body: Response.Body
            do {
                let encoder = try ContentConfiguration.global.requireEncoder(for: .json)
                var byteBuffer = req.byteBufferAllocator.buffer(capacity: 0)
                try encoder.encode(ErrorResponse(error: true, reason: reason), to: &byteBuffer, headers: &headers)
                
                body = .init(
                    buffer: byteBuffer,
                    byteBufferAllocator: req.byteBufferAllocator
                )
            } catch {
                body = .init(string: "Oops: \(String(describing: error))\nWhile encoding error: \(reason)", byteBufferAllocator: req.byteBufferAllocator)
                headers.contentType = .plainText
            }
            
            // create a Response with appropriate status
            return Response(status: status, headers: headers, body: body)
        }
    }


Review comment (suggestion):
A nitpick, but the docs ask to prefer not using the `.string(...)` syntax? https://apple.github.io/swift-log/docs/current/Logging/Structs/Logger/MetadataValue.html

Code after:
    public static func `default`(environment: Environment) -> ErrorMiddleware {
        return .init { req, error in
            let status: HTTPResponseStatus, reason: String, source: ErrorSource
            var headers: HTTPHeaders

            // Inspect the error type and extract what data we can.
            switch error {
            case let debugAbort as (DebuggableError & AbortError):
                (reason, status, headers, source) = (debugAbort.reason, debugAbort.status, debugAbort.headers, debugAbort.source ?? .capture())
                
            case let abort as AbortError:
                (reason, status, headers, source) = (abort.reason, abort.status, abort.headers, .capture())
            
            case let debugErr as DebuggableError:
                (reason, status, headers, source) = (debugErr.reason, .internalServerError, [:], debugErr.source ?? .capture())
            
            default:
                // In debug mode, provide the error description; otherwise hide it to avoid sensitive data disclosure.
                reason = environment.isRelease ? "Something went wrong." : String(describing: error)
                (status, headers, source) = (.internalServerError, [:], .capture())
            }
            
            // Report the error
            req.logger.report(error: error,
                              metadata: ["method" : "\(req.method.string)",
                                         "url" : "\(req.url.string)",
                                         "userAgent" : .array(req.headers["User-Agent"].map { "\($0)" })],
                              file: source.file,
                              function: source.function,
                              line: source.line)
            
            // attempt to serialize the error to json
            let body: Response.Body
            do {
                let encoder = try ContentConfiguration.global.requireEncoder(for: .json)
                var byteBuffer = req.byteBufferAllocator.buffer(capacity: 0)
                try encoder.encode(ErrorResponse(error: true, reason: reason), to: &byteBuffer, headers: &headers)
                
                body = .init(
                    buffer: byteBuffer,
                    byteBufferAllocator: req.byteBufferAllocator
                )
            } catch {
                body = .init(string: "Oops: \(String(describing: error))\nWhile encoding error: \(reason)", byteBufferAllocator: req.byteBufferAllocator)
                headers.contentType = .plainText
            }
            
            // create a Response with appropriate status
            return Response(status: status, headers: headers, body: body)
        }
    }