How to get Safari browser logs on iOS?

When running automated tests on Safari for iOS devices, capturing browser logs is essential for debugging JavaScript errors, monitoring network requests, and diagnosing issues. Appium provides several log types that give you insight into what's happening inside Safari during your test sessions.

Available Safari Log Types

When testing Safari on iOS through Appium, the following log types are available:

Log Type Description
safariConsole JavaScript console output from Safari, including errors, warnings and log messages
safariNetwork Network request and response logs from Safari, useful for debugging API calls and resource loading
syslog iOS system log messages from the device or simulator
crashlog Crash reports from the device or simulator
performance Performance timing data for page loads and resource fetches
server Appium server log output

Appium Capabilities for Safari Logging

To enable Safari log capture, you need to set specific Appium capabilities in your test configuration:

Capability Description
appium:showSafariConsoleLog Set to true to enable capturing Safari console logs
appium:showSafariNetworkLog Set to true to enable capturing Safari network logs
Important: Without setting these capabilities, fetching Safari logs will result in an error: "No logs currently available." Always enable the log types you need before starting your session.

JavaScript / Node.js Example

const { Builder } = require('selenium-webdriver');

  (async () => {
    const driver = await new Builder()
      .usingServer('https://key:secret@hub.testingbot.com/wd/hub')
      .withCapabilities({
        browserName: 'safari',
        browserVersion: '18.6',
        'appium:deviceName': 'iPhone 16',
        platformName: 'iOS',
        'tb:name': 'Safari Log Test',
        'appium:showSafariConsoleLog': true,
        'appium:showSafariNetworkLog': true
      })
      .build();

    try {
      await driver.navigate().to('https://testingbot.com');
      await driver.sleep(3000);

      const types = await driver.manage().logs().getAvailableLogTypes();
      console.log('Available log types:', types);

      const consoleLogs = await driver.manage().logs().get('safariConsole');
      console.log('Safari console logs:');
      consoleLogs.forEach(entry => {
        console.log(`[${entry.level}] ${entry.timestamp} - ${entry.message}`);
      });

      const networkLogs = await driver.manage().logs().get('safariNetwork');
      console.log('Safari network logs:');
      networkLogs.forEach(entry => {
        console.log(`[${entry.level}] ${entry.message}`);
      });
    } finally {
      await driver.quit();
    }
  })();

Python Example

from selenium import webdriver
  import time

  capabilities = {
      "browserName": "safari",
      "browserVersion": "18.6",
      "appium:deviceName": "iPhone 16",
      "platformName": "iOS",
      "tb:name": "Safari Log Test",
      "appium:showSafariConsoleLog": True,
      "appium:showSafariNetworkLog": True
  }

  driver = webdriver.Remote(
      command_executor="https://key:secret@hub.testingbot.com/wd/hub",
      desired_capabilities=capabilities
  )

  try:
      driver.get("https://testingbot.com")
      time.sleep(3)

      available_types = driver.log_types
      print("Available log types:", available_types)

      console_logs = driver.get_log("safariConsole")
      print("Safari console logs:")
      for entry in console_logs:
          print(f"[{entry['level']}] {entry['timestamp']} - {entry['message']}")

      network_logs = driver.get_log("safariNetwork")
      print("Safari network logs:")
      for entry in network_logs:
          print(f"[{entry['level']}] {entry['message']}")
  finally:
      driver.quit()

Java Example

import org.openqa.selenium.*;
  import org.openqa.selenium.remote.*;
  import org.openqa.selenium.logging.*;
  import java.net.URL;
  import java.util.Set;

  public class SafariLogTest {
      public static void main(String[] args) throws Exception {
          DesiredCapabilities capabilities = new DesiredCapabilities();
          capabilities.setCapability("browserName", "safari");
          capabilities.setCapability("browserVersion", "18.6");
          capabilities.setCapability("appium:deviceName", "iPhone 16");
          capabilities.setCapability("platformName", "iOS");
          capabilities.setCapability("tb:name", "Safari Log Test");
          capabilities.setCapability("appium:showSafariConsoleLog", true);
          capabilities.setCapability("appium:showSafariNetworkLog", true);

          RemoteWebDriver driver = new RemoteWebDriver(
              new URL("https://key:secret@hub.testingbot.com/wd/hub"),
              capabilities
          );

          try {
              driver.get("https://testingbot.com");
              Thread.sleep(3000);

              Set<String> logTypes = driver.manage().logs().getAvailableLogTypes();
              System.out.println("Available log types: " + logTypes);

              LogEntries consoleLogs = driver.manage().logs().get("safariConsole");
              System.out.println("Safari console logs:");
              for (LogEntry entry : consoleLogs) {
                  System.out.printf("[%s] %s - %s%n",
                      entry.getLevel(), entry.getTimestamp(), entry.getMessage());
              }

              LogEntries networkLogs = driver.manage().logs().get("safariNetwork");
              System.out.println("Safari network logs:");
              for (LogEntry entry : networkLogs) {
                  System.out.printf("[%s] %s%n", entry.getLevel(), entry.getMessage());
              }
          } finally {
              driver.quit();
          }
      }
  }

C# Example

using OpenQA.Selenium;
  using OpenQA.Selenium.Remote;

  var capabilities = new DesiredCapabilities();
  capabilities.SetCapability("browserName", "safari");
  capabilities.SetCapability("browserVersion", "18.6");
  capabilities.SetCapability("appium:deviceName", "iPhone 16");
  capabilities.SetCapability("platformName", "iOS");
  capabilities.SetCapability("tb:name", "Safari Log Test");
  capabilities.SetCapability("appium:showSafariConsoleLog", true);
  capabilities.SetCapability("appium:showSafariNetworkLog", true);

  var driver = new RemoteWebDriver(
      new Uri("https://key:secret@hub.testingbot.com/wd/hub"),
      capabilities,
      TimeSpan.FromSeconds(300)
  );

  try
  {
      driver.Navigate().GoToUrl("https://testingbot.com");
      Thread.Sleep(3000);

      var types = driver.Manage().Logs.AvailableLogTypes;
      Console.WriteLine("Available log types:");
      foreach (var type in types)
      {
          Console.WriteLine(type);
      }

      var consoleLogs = driver.Manage().Logs.GetLog("safariConsole");
      Console.WriteLine("Safari console logs:");
      foreach (var entry in consoleLogs)
      {
          Console.WriteLine($"[{entry.Level}] {entry.Timestamp} - {entry.Message}");
      }

      var networkLogs = driver.Manage().Logs.GetLog("safariNetwork");
      Console.WriteLine("Safari network logs:");
      foreach (var entry in networkLogs)
      {
          Console.WriteLine($"[{entry.Level}] {entry.Message}");
      }
  }
  finally
  {
      driver.Quit();
  }

Ruby Example

require "selenium-webdriver"

  caps = Selenium::WebDriver::Remote::Capabilities.new
  caps["browserName"] = "safari"
  caps["browserVersion"] = "18.6"
  caps["appium:deviceName"] = "iPhone 16"
  caps["platformName"] = "iOS"
  caps["tb:name"] = "Safari Log Test"
  caps["appium:showSafariConsoleLog"] = true
  caps["appium:showSafariNetworkLog"] = true

  http_client = Selenium::WebDriver::Remote::Http::Default.new
  http_client.read_timeout = 300
  http_client.open_timeout = 300

  driver = Selenium::WebDriver.for(
    :remote,
    url: "https://key:secret@hub.testingbot.com/wd/hub",
    options: caps,
    http_client: http_client
  )

  begin
    driver.navigate.to "https://testingbot.com"
    sleep 3

    bridge = driver.send(:bridge)
    session_id = bridge.session_id

    types = bridge.http.call(:get, "session/#{session_id}/se/log/types", nil)
    puts "Available log types:"
    puts types.inspect

    logs = bridge.http.call(:post, "session/#{session_id}/se/log", { type: "safariConsole" })
    puts "Safari console logs:"
    logs["value"].each do |entry|
      puts "[#{entry['level']}] #{entry['timestamp']} - #{entry['message']}"
    end

    network_logs = bridge.http.call(:post, "session/#{session_id}/se/log", { type: "safariNetwork" })
    puts "Safari network logs:"
    network_logs["value"].each do |entry|
      puts "[#{entry['level']}] #{entry['message']}"
    end
  ensure
    driver.quit
  end
Note: In Ruby Selenium 4.38+, the manage.logs API has been removed. The Ruby example above uses direct HTTP calls to the WebDriver log endpoints as a workaround.

Understanding Log Output

Each log entry contains the following fields:

  • level: The severity level (INFO, WARNING, SEVERE)
  • timestamp: Unix timestamp of when the log entry was created
  • message: The log message content

safariConsole Output

Console logs capture JavaScript output including:

  • console.log(), console.warn(), and console.error() calls
  • Unhandled JavaScript exceptions
  • Content Security Policy violations
  • Deprecation warnings from Safari

safariNetwork Output

Network logs capture HTTP activity including:

  • Request URLs, methods and headers
  • Response status codes and timing
  • Failed requests and timeouts
  • Resource loading errors

Using Safari Logs with TestingBot

When running Safari iOS tests on TestingBot, logs are also available through the TestingBot dashboard. You can view logs for completed test sessions without needing to fetch them programmatically.

For real-time log access during test execution, use the code examples above with the appium:showSafariConsoleLog and appium:showSafariNetworkLog capabilities enabled.

TestingBot Logo

Sign up for a Free Trial

Start testing your apps with TestingBot.

No credit card required.

Start Free Trial

Other Questions