How to control logging and debug verbosity in Traffic Parrot

« Back to documentation home

Logging and debug verbosity overview

Traffic Parrot is a standalone mock and service virtualization server that is compatible with WireMock stub mappings. If you are coming from WireMock and searching for how to enable verbose logging, change the log level, turn off debug output, or simply find where the logs are, this page is the Traffic Parrot answer.

The key difference to keep in mind: in Traffic Parrot you control logging by editing a logging configuration file in the install directory and then restarting the server. There is no --verbose command-line flag; unlike WireMock standalone, Traffic Parrot does not take a verbosity switch on the launch command. The log level lives in the configuration file.

It also helps to know that Traffic Parrot has two distinct places where it records what happened to a request:

  • The log files on disk (and console output): controlled by the logging configuration file described on this page. This is where startup, errors, and per-request entries are written.
  • The in-memory Request Log in the web UI: a live journal of every request with full headers, bodies, timing, and a field-by-field near-miss comparison for unmatched requests. This is the best place to answer "why didn't my request match?".

The sections below cover both, starting with the files on disk.

Where are the logs?

Traffic Parrot writes its log files to a logs folder inside the install directory (the folder you unzipped, the same place that contains start.sh and the configuration files). The default destinations are:

Location What it contains
logs/trafficparrot.log The main application log: startup, configuration, errors, and one entry per request received by the virtual service. This is a rolling file (1 MB per file, up to 100 backups), so older entries roll into trafficparrot.log.1, trafficparrot.log.2, and so on.
tp.out Console / boot output captured at the install root when you start Traffic Parrot. Useful for diagnosing problems that occur before logging is fully initialised.
logs/http-ui-access/
logs/http-vs-access/
Jetty HTTP access logs (one request per line) for the management UI port and the virtual service port respectively. See turning logging down to disable these.

When you start Traffic Parrot in the foreground with start-foreground.sh, the application log is also surfaced on the console so you can watch it live. When you start it in the background with start.sh, the application log stream is not duplicated to the background process's standard output; read logs/trafficparrot.log directly in that case.

Enabling verbose logging and changing the log level

The log level for the files and console is controlled by a single configuration file that ships in the install directory:

trafficparrotserver.log4j.properties

This file sits at the install root (the folder you unzipped); there is no bin subdirectory. It is a standard log4j 1.x properties file. The shipped default looks like this:

log4j.rootLogger=INFO,file,stdout

log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=logs/trafficparrot.log
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=100
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %t %m%n----------------------------------------------------------------------%n
log4j.appender.file.encoding=UTF8

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %t %m%n----------------------------------------------------------------------%n
log4j.appender.stdout.encoding=UTF8

log4j.logger.WireMock=INFO
log4j.logger.dorkbox.util.jna=ERROR
log4j.logger.dorkbox.systemTray=ERROR
log4j.logger.org.eclipse.jetty.util.ssl.SslContextFactory=WARN
log4j.logger.org.glassfish.jersey.internal.inject.Providers=ERROR
log4j.logger.io.swagger.v3.parser.OpenAPIV3Parser=ERROR

log4j.category.com.trafficparrot.messaging.ibmmq.connection.monitor.ReportingIbmMqPerformanceMonitor=INFO

The first line is the one you change to raise or lower verbosity. The value before the comma is the level (TRACE, DEBUG, INFO, WARN, ERROR); the names after it are the appenders (here file and stdout) that the level applies to.

To turn on full verbose / debug logging

Change the root logger from INFO to DEBUG:

log4j.rootLogger=DEBUG,file,stdout

Then restart Traffic Parrot.

Raising the level for one category only

Rather than turning everything up, you can keep the root logger at INFO and raise the level of a single category. For example, lines such as the third-party suppressors above raise (quieten) specific packages to WARN or ERROR.

Seeing why an HTTP request didn't match

This is the most common reason WireMock users go looking for verbose logging: a request returned a 404 / "not matched" response and they want to know which part of the stub didn't line up. In Traffic Parrot the answer is not in the log file; it is in the GUI Request Log.

What the log file shows

For an unmatched HTTP request, logs/trafficparrot.log records that a request arrived and that no stub was configured for it, but it does not contain a field-by-field comparison. A real entry looks like this:

2026-05-31 03:43:00,438 INFO  http-vs-Default-136 Request received:
[0:0:0:0:0:0:0:1] - GET /api/users

Host: [localhost:18281]
Accept: [text/plain]


Matched response definition:
(no response definition configured)

Response:
HTTP/1.1 404
(no headers)
----------------------------------------------------------------------

That tells you the request reached Traffic Parrot and no mapping matched it, but not why. For the "why", use the Request Log.

The GUI Request Log near-miss view

Open the HTTP Request Log from the HTTP dropdown in the top navigation bar (the Requests page). Every request the virtual service has received is listed, with a green Yes badge for matched requests and a red No badge for unmatched ones.

Expand the unmatched (red No) row to reveal the request headers, the response status, and a Near Misses section. Each near miss is a stub that came close to matching, shown as a comparison table with an Expected (stub) column, an Actual (request) column, a match-percentage badge (for example 96% match), and a View Stub link to jump straight to editing that stub. A green tick marks each field that matched; a red cross (and a highlighted row) marks each field that did not.

In the example above the stub expects GET /api/users with Accept: application/json, but the request sent Accept: text/plain. The method and URL path rows match (green ticks); the Header: Accept row is highlighted with a red cross, making it immediately clear why the request was not matched. See the HTTP page for the full reference on near misses for unmatched requests.

The same information programmatically

If you prefer to read the journal from a script, request the recorded requests through the management API. Each entry includes a "wasMatched": false flag for unmatched requests and a subEvents entry of type REQUEST_NOT_MATCHED:

curl http://localhost:8080/__admin/requests

Replace 8080 with your trafficparrot.gui.http.port if you changed it. See the programmatic setup tutorial for more management-API examples.

Traffic Parrot started but one of my mocks or extensions is missing

One item that cannot be loaded does not stop Traffic Parrot from starting, and does not make every request fail. The item is left out, logged and shown, and everything else is served:

  • An HTTP or gRPC mapping file that is not valid JSON, is not a mapping, is empty, or names a request body file under __files that is missing. The other mappings, in that file's directory and elsewhere, are served. A trafficparrot.sh validate run still fails on such a file.
  • A class named in trafficparrot.http.responsetransformers that cannot be loaded or created: it is not on the classpath, it has neither a public no-argument constructor nor a public constructor taking a TrafficParrotServices, or its constructor throws (a jar that carries its own copy of the SDK, or one built for a newer Java, fails this way). The other extensions and every mapping are served. The main HTTP port and each scenario served on its own port load the extensions separately, so a class can be left out on one of them and not another.
What the log file shows

Each item is an ERROR in logs/trafficparrot.log naming it and the reason. A mapping file's is written when it is left out and again only if the reason changes. An extension's is written each time a server starts without it: once for the main port and once per scenario served on its own port at start, and again when recording or proxying stops, which restarts the server. The samples below are trimmed: the shipped pattern adds the timestamp before ERROR and the thread name after it, and an extension left out with a cause is followed by its stack trace:

ERROR Skipping mapping file /opt/trafficparrot/mappings/broken.json, the other mappings are still served: Unrecognized token 'not': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
ERROR Skipping HTTP extension com.example.NotOnTheClasspath, the other extensions and the mappings are still served: it was not found on the classpath
What the mappings list shows

The HTTP and gRPC mappings lists show a notice above the table with one line per item. A left-out mapping file is listed as mapping file <path>: <reason> under the heading Left out because they could not be loaded; every other mapping is served, naming the file exactly as the log does so the line can be searched for there; on the HTTP list a left-out extension follows as HTTP extension <class> was left out: <reason>, and when only extensions are left out the notice is those lines alone, without the heading. The notice is refreshed with the list's automatic reload and goes once the item loads.

The same information programmatically

Three management endpoints answer the same lists as JSON, an array of {"skipped": "...", "reason": "..."} that is empty when nothing is left out:

curl http://localhost:8080/http/management/skippedMappingFiles
curl http://localhost:8080/grpc/management/skippedMappingFiles
curl http://localhost:8080/http/management/skippedExtensions

The file lists are current: a fixed file drops off on the next load and a newly broken one joins. The extension list is current as of each WireMock server's last start, one entry per class and reason in the property's order, and a class left out on only some servers has its reason end with them, for example ; on the main port (8081) or ; on scenario orders (port 8091). Replace 8080 with your trafficparrot.gui.http.port if you changed it. The three are in the OpenAPI reference.

Turning logging down or off

To make Traffic Parrot quieter, do the reverse of enabling verbose logging: raise the level so fewer messages are written. In trafficparrotserver.log4j.properties, set the root logger (or specific categories) to WARN or ERROR, then restart:

# Only warnings and errors in the log file
log4j.rootLogger=WARN,file,stdout
# The WireMock category writes the per-request entries and is set to INFO in the shipped file
log4j.logger.WireMock=WARN

Two related settings live in trafficparrot.properties (also at the install root) rather than in the logging file:

Property Default Effect
trafficparrot.virtualservice.accessLogsEnabled true Set to false to stop writing the Jetty access logs under logs/http-ui-access/ and logs/http-vs-access/.
trafficparrot.virtualservice.httpRequestLoggingDisabled false Set to true to stop writing the Request received: entry that logs/trafficparrot.log gets for each request to the HTTP virtual service (useful for maximum throughput in performance tests). The in-memory request journal that powers the Request Log is separate: trafficparrot.virtualservice.maxRequestJournalEntries sizes it, and 0 switches it off.

Both of these are standard Traffic Parrot properties; see the Configuration Reference for the full list and how to edit trafficparrot.properties.

Using Logback or Log4j2 instead

Traffic Parrot uses the log4j 1.x properties file by default, but it also ships two alternative configuration files at the install root for teams that prefer a different logging framework:

  • trafficparrotserver.logback.xml: Logback configuration
  • trafficparrotserver.log4j2.xml: Log4j2 configuration

To switch, edit the jvm.args file (also at the install root) and point the -Dtrafficparrotserver.logging.properties.filename= argument at the file you want, then restart. For example:

-Dtrafficparrotserver.logging.properties.filename=trafficparrotserver.logback.xml

Most users do not need to do this; editing the default trafficparrotserver.log4j.properties as shown above is sufficient. See JVM arguments for more about the jvm.args file.

Next steps