如何以编程方式读取wkwebview的控制台日志

Nav*_*mar 15 javascript uiwebview ios swift wkwebview

我正在尝试以编程方式读取以我的WkWebview加载的webapp的控制台日志.

到目前为止在我的研究中它是不可能的.

任何帮助实现这一点将不胜感激!

小智 19

这对我有用(Swift 4.2/5):

// inject JS to capture console.log output and send to iOS
let source = "function captureLog(msg) { window.webkit.messageHandlers.logHandler.postMessage(msg); } window.console.log = captureLog;"
let script = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
webView.configuration.userContentController.addUserScript(script)
// register the bridge script that listens for the output
webView.configuration.userContentController.add(self, name: "logHandler")
Run Code Online (Sandbox Code Playgroud)

然后,按照 WKScriptMessageHandler 协议,使用以下内容获取重定向的控制台消息:

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    if message.name == "logHandler" {
        print("LOG: \(message.body)")  
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这对我有用(iOS 13.5,Objective-C,Xcode 11.5) (2认同)

Ode*_*gev 18

可以将Mac上的Safari浏览器连接到WKWebView并访问控制台。

在Safari中,打开“开发”标签,然后在运行WKWebView的状态下运行iOS Simulator时,只需单击它即可打开控制台。看到:

在此处输入图片说明

  • 即使模拟器正在运行,我的 Safari 的开发菜单中也没有“模拟器”选项。我有最新的 Xcode 和最新的 Safari。几个月来一直如此。 (4认同)
  • 我在 wkwebview 中看到空控制台,但是如果我在 safari 中打开该链接,我会看到日志。:( (2认同)
  • 点击顶部菜单中的 Safari,从那里:Safari --> 首选项 --> 高级 --> “显示开发菜单” (2认同)
  • 这不是以编程方式,不回答问题 (2认同)

Soe*_*olm 10

我需要一种在 Xcode 控制台中查看 JavaScript 日志的方法。根据 noxo 的回答,这是我想出的:

let overrideConsole = """
    function log(emoji, type, args) {
      window.webkit.messageHandlers.logging.postMessage(
        `${emoji} JS ${type}: ${Object.values(args)
          .map(v => typeof(v) === "undefined" ? "undefined" : typeof(v) === "object" ? JSON.stringify(v) : v.toString())
          .map(v => v.substring(0, 3000)) // Limit msg to 3000 chars
          .join(", ")}`
      )
    }

    let originalLog = console.log
    let originalWarn = console.warn
    let originalError = console.error
    let originalDebug = console.debug

    console.log = function() { log("", "log", arguments); originalLog.apply(null, arguments) }
    console.warn = function() { log("", "warning", arguments); originalWarn.apply(null, arguments) }
    console.error = function() { log("", "error", arguments); originalError.apply(null, arguments) }
    console.debug = function() { log("", "debug", arguments); originalDebug.apply(null, arguments) }

    window.addEventListener("error", function(e) {
       log("", "Uncaught", [`${e.message} at ${e.filename}:${e.lineno}:${e.colno}`])
    })
"""

class LoggingMessageHandler: NSObject, WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        print(message.body)
    }
}

let userContentController = WKUserContentController()
userContentController.add(LoggingMessageHandler(), name: "logging")
userContentController.addUserScript(WKUserScript(source: overrideConsole, injectionTime: .atDocumentStart, forMainFrameOnly: true))

let webViewConfig = WKWebViewConfiguration()
webViewConfig.userContentController = userContentController

let webView = WKWebView(frame: .zero, configuration: webViewConfig)
Run Code Online (Sandbox Code Playgroud)

它有一些改进:

  • 它仍然调用原始日志函数,以防您决定查看 Web Inspector
  • 它从log、warn、error和debug
  • 它添加了一个漂亮的表情符号,因此您可以轻松区分不同类型的 og 日志和 JS 日志在 Xcode 控制台中脱颖而出
  • 它记录给 的所有参数console.log,而不仅仅是第一个
  • 它会记录未捕获的错误,以防您需要


nox*_*oxo 7

您可以重新评估(覆盖)JavaScript console.log()默认实现,以使用window.webkit.messageHandlers.postMessage(msg)来传递消息转发。然后使用WKScriptMessageHandler :: didReceiveScriptMessage在本机代码处拦截javascript postMessage(msg)调用,以获取记录的消息。

步骤1)重新评估console.log的默认实现以使用postMessage()

// javascript to override console.log to use messageHandlers.postmessage
NSString * js = @"var console = { log: function(msg){window.webkit.messageHandlers.logging.postMessage(msg) } };";
// evaluate js to wkwebview
[self.webView evaluateJavaScript:js completionHandler:^(id _Nullable ignored, NSError * _Nullable error) {
    if (error != nil)
        NSLog(@"installation of console.log() failed: %@", error);
}];
Run Code Online (Sandbox Code Playgroud)

步骤2)在WKScriptMessageHandler :: didReceiveScriptMessage的本机代码中拦截javascript postMessage

- (void)viewDidLoad
{
    // create message handler named "logging"
    WKUserContentController *ucc = [[WKUserContentController alloc] init];
    [ucc addScriptMessageHandler:self name:@"logging"];
    // assign usercontentcontroller to configuration    
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    [configuration setUserContentController:ucc];
    // assign configuration to wkwebview    
    self.webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) configuration:configuration];
}


- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
    // what ever were logged with console.log() in wkwebview arrives here in message.body property
    NSLog(@"log: %@", message.body);
}
Run Code Online (Sandbox Code Playgroud)

  • 有没有其他人让这个解决方案起作用(或下面的另一个?)。我尝试了...didReceiveScriptMessage: 从来没有被调用过。 (2认同)

bio*_*ker 6

此页面上的一些其他出色的解决方案可能会神秘地使您的页面崩溃。我发现这是因为某些对象(例如事件)导致 JSON.stringify 抛出异常(例如参见如何对事件对象进行字符串化?)

为了简单起见,我捕获了异常并继续。当我这样做时,我将逻辑封装到一个类中,以便 userContentController 对象的使用变得简单:

        WebLogHandler().register(with: userContentController)
Run Code Online (Sandbox Code Playgroud)

实现的来源如下。我注入的脚本比这里的一些脚本更简单,出于我的目的,我实际上不想听到警告/错误等,所以我可以专注于日志,并且我不想看到任何表情符号,但除此之外,这与 Soeholm 的类似回答。这是一个练习,可以使用这些选项使类更加可配置,或者巧妙地处理有问题的对象,以便至少部分地对它们进行字符串化。

   class WebLogHandler: NSObject, WKScriptMessageHandler {
        
        let messageName = "logHandler"
    
        lazy var scriptSource:String = {
            return """
    
            function stringifySafe(obj) {
                try {
                    return JSON.stringify(obj)
                }
                catch (err) {
                    return "Stringify error"
                }
            }
    
            function log(type, args) {
              window.webkit.messageHandlers.\(messageName).postMessage(
                `JS ${type}: ${Object.values(args)
                  .map(v => typeof(v) === "undefined" ? "undefined" : typeof(v) === "object" ? stringifySafe(v) : v.toString())
                  .map(v => v.substring(0, 3000)) // Limit msg to 3000 chars
                  .join(", ")}`
              )
            }
    
            let originalLog = console.log
    
            console.log = function() { log("log", arguments); originalLog.apply(null, arguments) }
            """
            
        }()
    
    
        func register(with userContentController: WKUserContentController) {
            userContentController.add(self, name: messageName)
            
            // inject JS to capture console.log output and send to iOS
            let script = WKUserScript(source: scriptSource,
                                      injectionTime: .atDocumentStart,
                                      forMainFrameOnly: false)
            userContentController.addUserScript(script)
        }
        
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            
            print(message.body)
        }
    }
Run Code Online (Sandbox Code Playgroud)


xap*_*hod 5

这是理查德的答案的一个调整(好吧,几个答案使用相同的方法)来处理 的console.log字符串替换。

我需要这个,因为 React 错误是用像 之类的字符串记录的Hey you had an error:%s,当然我需要看看是什么%s。我也使用了.documentStart,因为我想立即捕获错误。

添加WKUserScript到(这是你初始化WKUserContentController的一部分):WKWebViewConfigurationWKWebView

        let source = """
        function sprintf(str, ...args) { return args.reduce((_str, val) => _str.replace(/%s|%v|%d|%f|%d/, val), str); }
        function captureLog(str, ...args) { var msg = sprintf(str, ...args); window.webkit.messageHandlers.logHandler.postMessage({ msg, level: 'log' }); }
        function captureWarn(str, ...args) { var msg = sprintf(str, ...args); window.webkit.messageHandlers.logHandler.postMessage({ msg, level: 'warn' }); }
        function captureError(str, ...args) { var msg = sprintf(str, ...args); window.webkit.messageHandlers.logHandler.postMessage({ msg, level: 'error' }); }
        window.console.error = captureError; window.console.warn = captureWarn;
        window.console.log = captureLog; window.console.debug = captureLog; window.console.info = captureLog;
        """
        let script = WKUserScript(source: source, injectionTime: .atDocumentStart, forMainFrameOnly: false)

        let config = WKWebViewConfiguration.init()
        let userContentController = WKUserContentController()
        config.userContentController = userContentController
        userContentController.addUserScript(script)

        // ... potentially somewhere else ...
        let webView = WKWebView(frame: CGRect.zero, configuration: config)
Run Code Online (Sandbox Code Playgroud)

NSObject...并在您实现的任何类中处理它(提示:必须是一个) WKScriptMessageHandler:

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        guard
            let body = message.body as? [String:Any],
            let msg = body["msg"] as? String,
            let level = body["level"] as? String
        else {
            assert(false)
            return
        }
        NSLog("WEB \(level): \(msg)")
}
Run Code Online (Sandbox Code Playgroud)

替换NSLog为您实际使用的任何内容(即您的自定义 Swift 日志处理程序函数)。


ite*_*ian -7

请使用这个美丽的应用内“桥梁”

编辑:

self.webView = [[WBWKWebView alloc] initWithFrame:self.view.bounds];
self.webView.JSBridge.interfaceName = @"WKWebViewBridge";
WBWebDebugConsoleViewController * controller = [[WBWebDebugConsoleViewController alloc] initWithConsole:_webView.console];
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用委托方法:

- (void)webDebugInspectCurrentSelectedElement:(id)sender
{
// To use the referenced log values
}
Run Code Online (Sandbox Code Playgroud)