Skip to main content

RUNNING TESTS

Logging

Karate v2 has a single bucket for everything log-related: configure logging. It controls what gets captured into reports, what hits the console, how HTTP bodies are formatted, and which fields get redacted.

TL;DR

karate-config.js
function fn() {
karate.configure('logging', {
report: 'debug', // what gets captured into reports
console: 'info', // what hits stdout via SLF4J/Logback
pretty: true, // pretty-print HTTP JSON bodies
mask: { // HTTP-only redaction
headers: ['Authorization', 'Cookie'],
jsonPaths: ['$.password', '$..token'],
patterns: [{ regex: '\\bBearer [A-Za-z0-9._-]+\\b', replacement: 'Bearer ***' }]
}
});
return { baseUrl: 'https://api.example.com' };
}

The same shape works inside a feature file:

Background:
* configure logging = { report: 'debug', console: 'info', pretty: true }

Two thresholds: report vs console

Karate keeps two independent levels.

KnobWhat it controlsCLI flagDefault
logging.reportWhat gets captured into HTML / JSONL / Cucumber JSON / JUnit--log-reportdebug
logging.consoleWhat hits stdout via SLF4J / Logback--log-consoleinfo

The HTTP logger always writes the full request/response (headers + bodies) to the report buffer at INFO. The console emission tiers automatically by SLF4J level: INFO = one-liner, DEBUG = +headers, TRACE = +body. Splitting the two knobs lets you, for example, capture full traces in reports for post-hoc debugging while keeping a parallel run's console quiet.

# Quiet console, rich reports — typical CI setup
* configure logging = { report: 'debug', console: 'warn' }

# Reverse — chatty local debugging, lean reports
* configure logging = { report: 'warn', console: 'debug' }

Mid-test level flips with auto-restore

* configure logging = { ... } mid-flow takes effect immediately. At scenario end the levels are automatically snapshotted and restored, so the next scenario starts at whatever karate-config.js set. No manual cleanup needed.

Scenario: silence a noisy reusable
* configure logging = { report: 'error' }
* call read('classpath:noisy-warmup.feature')
# report level is back to default at scenario end — automatically

Pretty body formatting

logging.pretty applies to HTTP request/response JSON bodies in both console and report.

* configure logging = { pretty: true }    # default — multi-line, 2-space indent
* configure logging = { pretty: false } # single line

Non-JSON bodies (XML, plain text, binary) pass through unchanged. The pretty pass runs after masking, so masked values stay masked.

Masking sensitive data

logging.mask is a declarative redactor that runs against HTTP request/response logs. It does not scan print or karate.log output — those are user-controlled channels. If a step might leak via print, raise logging.report to 'warn' to drop INFO captures, or tag the scenario @report=false.

Headers

Header names are matched case-insensitively (RFC 7230). Matched headers' values are replaced with replacement (default '***').

* configure logging = {
mask: { headers: ['Authorization', 'Cookie', 'X-Api-Key'] }
}

* header Authorization = 'Bearer abc.def.ghi'
# Logged as: Authorization: ***

JSON paths

Two simple forms are supported:

  • $.a.b.c — descend by key
  • $..foo — recursive search for any foo key at any depth

Matched values become replacement.

* configure logging = {
mask: { jsonPaths: ['$.password', '$..token', '$.user.ssn'] }
}

* request { username: 'alice', password: 'hunter2', meta: { token: 'shhh' } }
# Logged body:
# {
# "username": "alice",
# "password": "***",
# "meta": { "token": "***" }
# }

Regex patterns

Each rule is { regex, replacement }. Patterns run after header / JSON-path masking, so they catch anything those passes didn't.

* configure logging = {
mask: {
patterns: [
{ regex: 'Bearer [A-Za-z0-9._-]+', replacement: 'Bearer ***' },
{ regex: '\\b\\d{16}\\b', replacement: '****-****-****-****' }
]
}
}

URL-scoped masking

enableForUri(uri) is an optional JS predicate. When it returns falsy for a request, no masking applies — useful for excluding /health or other low-value endpoints from redaction so debugging stays easy.

* configure logging = {
mask: {
headers: ['Authorization'],
enableForUri: function(uri){ return uri.indexOf('/health') < 0 }
}
}

Customizing the replacement

* configure logging = {
mask: {
headers: ['Authorization'],
replacement: '<redacted>'
}
}
# Logged as: Authorization: <redacted>

Hide a scenario from reports — @report=false

Tag a scenario @report=false to keep it in the run (it still counts toward suite totals) but suppress its step detail from HTML / Cucumber JSON / JUnit XML / JSONL outputs.

@report=false
Scenario: warmup with sensitive credentials
* call read('classpath:auth/login.feature')

Behavior:

  • The scenario runs normally and contributes to pass / fail / skip counts.
  • HTML / Cucumber / JUnit / JSONL show the row + status only — no step text, no captured logs, no embedded screenshots.
  • Failures surface a redacted message: output suppressed by @report=false (full detail in runtime logs).
  • Full failure detail still hits SLF4J / Logback, so you can debug locally with --log-console debug.
  • Suppression propagates: any feature called from a @report=false scenario is also hidden.

Use this for any run that may include secrets in HTTP bodies or error messages and where the artifacts get uploaded somewhere (CI archives, S3, Slack, etc.).

Deep merge

configure logging deep-merges with the parent. A partial update keeps everything else.

# Background sets the global mask + pretty
Background:
* configure logging = { mask: { headers: ['Authorization'] }, pretty: false }

# A later scenario flips just the threshold — mask + pretty survive
Scenario:
* configure logging = { report: 'error' }

SLF4J categories

For finer console control beyond a single threshold, Karate emits under named SLF4J categories. Tune them in logback.xml or via your dependency-injected logger config.

CategoryWhat it covers
karate.runtimeFeature/scenario lifecycle, step execution, suite orchestration
karate.httpHTTP client — request/response bodies, headers, retries
karate.mockMock server — incoming requests, matched scenarios, responses
karate.serverEmbedded HTTP server — request/response logs
karate.scenarioUser output — print, karate.log(), scenario-scoped messages
karate.consoleCLI console output — summary, colors, progress

Example: DEBUG only for HTTP traffic, INFO everywhere else:

logback-test.xml
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder><pattern>%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern></encoder>
</appender>

<logger name="karate.http" level="DEBUG"/>

<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>

logging.console is a convenience that sets the karate parent logger's level. For per-category control, edit logback.xml directly.

Putting it together — environment-driven logging

karate-config.js
function fn() {
var env = karate.env || 'dev';

var config = { baseUrl: 'https://api.example.com' };

if (env === 'dev') {
karate.configure('logging', {
report: 'debug', console: 'debug',
pretty: true
});
} else if (env === 'ci') {
karate.configure('logging', {
report: 'debug', // capture rich detail in artifacts
console: 'warn', // keep CI logs quiet
pretty: true,
mask: {
headers: ['Authorization', 'Cookie', 'X-Api-Key'],
jsonPaths: ['$.password', '$..token', '$..secret'],
patterns: [
{ regex: 'Bearer [A-Za-z0-9._-]+', replacement: 'Bearer ***' }
]
}
});
}

return config;
}

CLI

# Set both thresholds at run time
karate run --log-report debug --log-console warn features/

# Or via karate-pom.json (`logging` block)
karate-pom.json
{
"paths": ["src/test/features"],
"logging": {
"report": "debug",
"console": "info"
}
}

Mask configuration lives in feature files / karate-config.js, not the CLI — it's declarative and belongs with the test code that knows what's sensitive.

See also

  • Test Reports — formats, output directories, CI publishing
  • Debugging — using captured logs to troubleshoot
  • Tags@report=false and other built-in tags
  • Configuration — the configure keyword and karate-config.js