Test2:...ss::Log(3pm)
π¦ NAME π¦
okay so bestie, this is Test2::Harness::Log β the 3pm perl man page, straight out of User Contributed Perl Documentation π
. Real talk: this module is the documentation for the Test2::Harness log file format. That's it, that's the whole job β¨.
πΈ DESCRIPTION πΈ
Test2::Harness β aka App::Yath β can produce a rich, complete log whenever you ask it to π«Ά. And this module? This module documents that log format. No cap, that's the entire vibe. πβ¨
π COMPRESSION π
Test2::Harness gives you three compression flavors for your log files, pick your fighter:
- uncompressed β raw, unfiltered, main character energy π
- compressed in gzip β cute, efficient, iykyk
- compressed in bzip2 β the other compressed option, also valid, we do not play favorites π
β¨ FORMAT β¨
So the log file is in jsonl format π¦. Translation: every single line is independently parseable as json. Like, fully independent. Each line = one single event that Test2::Harness processed during the run β.
These events land in the original order Test2::Harness processed them in β and heads up, that might not be chronological to when they were actually generated, because generation, collection, processing and rendering are all handled by different processes π§. It's giving many hands, one group chat.
A complete log gets terminated by the string "null", which is also valid json, slay π
. And if a log is missing that terminator? Bestie. It's considered an incomplete log. π©
π EVENTS π
Please note: older versions of Test2::Harness produced less complete events. This section covers all current fields β so if you're out here trying to handle very old logs, some of those fields may simply be missing π.
Every event is gonna have these fields:
{
"event_id" : "CD01CD30-D535-11EA-9B6A-D90F9664FE12",
"job_id" : 0,
"job_try" : null,
"run_id" : "CCF98E54-D535-11EA-915A-D70F9664FE12",
"stamp" : 1596423763.76517,
"facet_data" : {
"harness" : {
"event_id" : "CD01CD30-D535-11EA-9B6A-D90F9664FE12",
"job_id" : 0,
"job_try" : null,
"run_id" : "CCF98E54-D535-11EA-915A-D70F9664FE12"
},
...
}
}
event_id:"UUID_OR_STRING"β usually this is a UUID, but when UUIDs literally cannot be generated it might be some other unique identifier instead. Always a string, always. This may never beNULLβ and if it isNULL, that's a bug and you should report it, fr fr ππ¨.job_id:"0_OR_UUID_OR_STRING"β id0is special: it represents the test harness itself, not an actual test being run π. Normallyjob_idis a UUID, but it can be another unique string if UUID generation is disabled or unavailable.job_try:INTEGER_OR_NULLβ for"job_id => 0"this will beNULL. For any other job, this is an integer of0or greater β¨. It's0the first time a test job runs, and if a job gets re-run because it failed (or for literally any other reason), this gets incremented so you know which run you're looking at π. When a job re-runs it keeps the same job ID β that's how you tell events from each run apart. iykyk.run_id:"UUID_OR_STRING"β therun_idof the entire yath test run πβοΈ. This should be identical for every event in any given log. Consistency? Obsessed.stamp:UNIX_TIME_STAMPβ the timestamp of the event β°. This is NORMALLY set when the event is generated β however, if an event doesn't have its own timestamp, yath will give it one upon collection. When does an event not have a timestamp? When the test outputs TAP instead ofTest2::Eventobjects, or when a tool misbehaves somehow π. Tools, bestie, they're wild.facet_data:HASHβ this holds all the data of the event: whether an assertion was made, what file name and line number generated it, etc. π
On top of the event's original facets, Test2::Harness may inject the following facets β or generate completely new events to convey these facets π.
π« harness_final
This one contains the final summary data from the end of the test run. The receipts, basically π§Ύβ¨:
{
# Was the test run a success, or were there failures?
pass => $BOOL,
# What tests failed?
failed => [
[
$job_id, # Job id of the job that failed
$file, # Test filename
],
...
],
# What tests had to be retried, and did they eventually pass?
retried => [
[
$job_id, # Job id of the job that was retied
$tries, # Number of tries attempted
$file, # Test filename
$eventually_passed, # 'YES' if it eventually passed, 'NO' if no try ever passed.
],
...
],
# What tests setn a halt event (such as bail-out, or skip the rest)
halted => [
[
$job_id, # Job id of the test
$file, # Test filename
$halt, # Halt code
],
...
],
# What tests were never run (maybe because of a bail-out, or an internal error)
unseen => [
[
$job_id, # Job id of the test
$file, # Test filename
],
...
],
}
π harness_watcher
Internal use only, subject to change, do not rely on it. That's the whole note. Don't build your whole personality on it, bestie π.
πΈ harness_job
A hash representation of an Test2::Harness::Runner::Job object π«Ά.
Note: this is done via a transformation β several methods have their values stored in this hash when the original object doesn't directly store them. Sneaky but iconic.
π harness_job_end
{
file => $provided_path_to_test_file,
rel_file => $relative_path_to_test_file,
abs_file => $absolute_path_to_test_file,
fail => $BOOL,
retry => $INTEGER, # Number of retries left
stamp => $UNIX_TIMESTAMP, # Timestamp of when the test completed
# May not be present
skip => $STRING, # Reason test was skipped (if it was skipped)
times => $TIMING_DATA, # See below
}
The "times" field gets populated by calling data_dump() on an Test2::Harness::Auditor::TimeTracker object β±οΈπ.
π¦ harness_job_exit
This one represents when the test job exited πͺ:
{
exit => $WSTAT,
retry => $INTEGER
stamp => $UNIX_TIMESTAMP
}
π harness_job_fields
Extra data attached to the harness job β usually coming from an Test2::Harness::Plugin via inject_run_data() β¨.
β harness_job_launch
This facet is almost always in the same event as the "harness_job_start" facet π«Ά.
NOTE: while writing these docs the author wonders if this facet is unnecessary... and honestly? We love an honest king. π
{
stamp => $UNIX_TIMESTAMP,
rety => $INTEGER,
}
π harness_job_queued
This data is produced by the "queue_item" method in Test2::Harness::TestFile π¦.
This contains the data about a test job conveyed by the queue. It usually contains data that will later be used by Test2::Harness::Runner::Job π οΈ. It's better to use the "harness_job" facet instead, because that one contains the final data actually used to run the job π
.
The following 3 fields are the only ones likely to be useful to most people:
{
file => $ORIGINAL_PATH_TO_FILE,
job_id => $UUID_OR_STRING,
stamp => $UNIX_TIMESTAMP,
}
π harness_job_start
This facet gets sent in an event as soon as a job starts π. The data in this facet is mainly meant to convey the necessary info to a renderer so it can render the fact that a job started. Respect the renderer's journey.
{
file => $provided_path_to_test_file,
rel_file => $relative_path_to_test_file,
abs_file => $absolute_path_to_test_file,
stamp => $UNIX_TIMESTAMP, # Timestamp of when the test completed
job_id => $UUID_OR_STRING,
details => "Job UUID_OR_STRING started at $UNIX_TIMESTAMP",
}
π harness_run
A hash representation of an Test2::Harness::Run object πβοΈπ¨.
π SOURCE π
The source code repository for Test2-Harness lives right here, bestie:
http://github.com/Test-More/Test2-Harness/
Go star it, it's the least you can do π«Άβ
π MAINTAINERS π
Chad Granum <exodist@cpan.org>
β¨ AUTHORS β¨
Chad Granum <exodist@cpan.org>
π COPYRIGHT π
Copyright 2020 Chad Granum <exodist7@gmail.com>.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself π.
See http://dev.perl.org/licenses/
and that's the whole page, bestie, we ate and left no crumbs π½οΈπ
this was perl v5.40.1, dated 2026-05-10, and it was Test2:...ss::Log(3pm) the entire time πΈπ¦β¨
π¦ where this page comes from
- ubuntu stonking: libtest2-harness-perl 1.000172-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms