This guide covers Groovy println, print, printf, System.out, and formatted output with 14 practical examples. Complete guide to console output tested on Groovy 5.0.6.
“The first thing every programmer learns is how to print. The last thing they master is knowing what to print and where.”
Last Updated: July 2026 | Tested on: Groovy 5.0.6, Java 17+ | Difficulty: Beginner | Reading Time: 16 minutes
The groovy println statement is usually the first thing you write when learning the language, but there is a lot more to console output than that single method. Groovy gives you print, println, printf, System.out, System.err, and sprintf – each with its own use case for formatting, debugging, and logging.
Groovy gives you several ways to write output: print, println, printf, System.out, System.err, and even sprintf for formatting without printing. Each has its own use case. Understanding when to use which one separates beginners from developers who write clean, debuggable scripts.
In this post, we will cover every output method Groovy offers, with 14 tested examples that show exactly what gets printed and why. The examples cover printing strings, numbers, collections, maps, formatted data, and redirecting output for logging and debugging. If you want to add timing and pauses between your output statements, check out our next post on Groovy sleep and timing.
The diagram below shows how your output methods split across two separate streams, and how shell redirection sends stdout and stderr to different destinations:
Tip: click the diagram to open it full screen, then use the zoom and pan controls for a closer look.
Table of Contents
print vs println – The Core Difference
The difference between print and println in Groovy is simple: println adds a newline at the end, print does not. That is it. Both methods are added by Groovy’s GDK to every script and class, so you can call them anywhere without importing anything.
According to the Groovy Development Kit documentation, these methods are provided through the DefaultGroovyMethods class which adds them to java.lang.Object. This means every object in Groovy can be printed directly.
| Method | Newline | Formatting | Returns | Use Case |
|---|---|---|---|---|
print | No | Calls toString() | void | Inline output, progress indicators |
println | Yes | Calls toString() | void | Standard line-by-line output |
printf | No | Format string (%s, %d, etc.) | void | Formatted/aligned output |
System.out.print | No | Calls toString() | void | When you need the Java PrintStream |
System.err | Depends | Calls toString() | void | Error messages, separate stream |
sprintf | No | Format string | String | Format without printing |
Groovy println Syntax and Basic Usage
All output methods in Groovy are simple. A groovy println call does not even need parentheses, since Groovy allows omitting them for single-argument method calls. However, using parentheses is perfectly fine and sometimes clearer, especially when passing expressions.
Basic Syntax for All Output Methods
// println - with and without parentheses
println "Hello, World!"
println("Hello, World!")
// print - no newline
print "Hello, "
print "World!"
println() // just a newline
// printf - formatted output
printf "Name: %s, Age: %d%n", "Alice", 30
// System.out directly
System.out.println("Using System.out directly")
// System.err for errors
System.err.println("This goes to stderr")
Output
Hello, World! Hello, World! Hello, World! Name: Alice, Age: 30 Using System.out directly This goes to stderr
Notice that println() with no arguments just prints a blank line. This is handy for adding visual separation in your output.
14 Practical Output Examples
Let us walk through all the output methods Groovy provides, starting with the basics and building up to more advanced patterns.
Example 1: print vs println Side by Side
This is the most fundamental comparison. The print method keeps the cursor on the same line, while println moves to a new line after printing.
Example 1: print vs println
// print keeps everything on one line print "One " print "Two " print "Three" println() // newline to finish the line // println puts each on its own line println "One" println "Two" println "Three" // Mixing print and println print "Loading" print "." print "." print "." println " Done!"
Output
One Two Three One Two Three Loading... Done!
Use print when you want to build a line piece by piece. This is especially useful for progress indicators or inline formatting where you control exactly where the newline goes.
Example 2: Printing Different Data Types
Both print and println call toString() on whatever you pass them. This means you can print any object directly.
Example 2: Printing Different Data Types
// Strings
println "Hello, Groovy!"
// Numbers
println 42
println 3.14159
println 100_000_000
// Booleans
println true
println false
// null
println null
// GString interpolation
def name = "Groovy"
def version = 5
println "Welcome to ${name} ${version}!"
// Expressions inside interpolation
println "2 + 3 = ${2 + 3}"
println "Uppercase: ${'groovy'.toUpperCase()}"
Output
Hello, Groovy! 42 3.14159 100000000 true false null Welcome to Groovy 5! 2 + 3 = 5 Uppercase: GROOVY
Notice that println null prints the word “null” rather than throwing an exception. Groovy handles nulls gracefully in output methods.
Example 3: GString Interpolation in println
One of the biggest reasons println feels so natural in Groovy is GString interpolation. You can embed any expression inside ${...} within a double-quoted string.
Example 3: GString Interpolation
def firstName = "Alice"
def lastName = "Johnson"
def age = 28
def scores = [95, 88, 92]
// Simple variable interpolation
println "Name: ${firstName} ${lastName}"
// Short form - no braces needed for simple variables
println "Name: $firstName $lastName"
// Expressions
println "Average score: ${scores.sum() / scores.size()}"
// Method calls
println "Uppercase: ${firstName.toUpperCase()}"
println "Name length: ${firstName.length() + lastName.length()}"
// Ternary inside interpolation
println "Status: ${age >= 18 ? 'Adult' : 'Minor'}"
// Multi-line with interpolation
println """
User Profile:
Name: ${firstName} ${lastName}
Age: ${age}
Grade: ${scores.average() >= 90 ? 'A' : 'B'}
""".stripIndent().trim()
Output
Name: Alice Johnson Name: Alice Johnson Average score: 91.6666666667 Uppercase: ALICE Name length: 12 Status: Adult User Profile: Name: Alice Johnson Age: 28 Grade: A
Remember: interpolation only works with double-quoted strings. Single-quoted strings are plain java.lang.String and will print the ${} literally.
Example 4: printf – C-Style Formatted Output
Groovy’s printf works just like C’s printf or Java’s System.out.printf. It uses format specifiers to control how values are displayed. Unlike println, printf does not add a newline automatically – you need %n for that.
Example 4: printf Formatting
// Basic format specifiers printf "String: %s%n", "Hello" printf "Integer: %d%n", 42 printf "Float: %f%n", 3.14159 printf "Float (2 decimals): %.2f%n", 3.14159 printf "Boolean: %b%n", true // Width and alignment printf "Right-aligned: [%20s]%n", "Groovy" printf "Left-aligned: [%-20s]%n", "Groovy" printf "Zero-padded: [%010d]%n", 42 // Multiple arguments printf "Name: %-10s | Age: %3d | Score: %6.2f%n", "Alice", 28, 95.5 printf "Name: %-10s | Age: %3d | Score: %6.2f%n", "Bob", 35, 87.25 printf "Name: %-10s | Age: %3d | Score: %6.2f%n", "Charlie", 22, 91.0 // Hexadecimal and octal printf "Decimal: %d, Hex: %x, Octal: %o%n", 255, 255, 255 // Scientific notation printf "Scientific: %e%n", 123456.789 printf "Compact: %g%n", 123456.789
Output
String: Hello Integer: 42 Float: 3.141590 Float (2 decimals): 3.14 Boolean: true Right-aligned: [ Groovy] Left-aligned: [Groovy ] Zero-padded: [0000000042] Name: Alice | Age: 28 | Score: 95.50 Name: Bob | Age: 35 | Score: 87.25 Name: Charlie | Age: 22 | Score: 91.00 Decimal: 255, Hex: ff, Octal: 377 Scientific: 1.234568e+05 Compact: 123457
The printf method is ideal when you need perfectly aligned columns, padded numbers, or specific decimal precision. Use %n instead of \n for platform-independent line endings.
Example 5: System.out Direct Access
Since Groovy runs on the JVM, you have full access to System.out. This is the same java.io.PrintStream that Java uses. Most of the time, Groovy’s built-in print and println delegate to System.out anyway, but there are times when using it directly makes sense.
Example 5: System.out Methods
// Standard System.out methods - same as Java
System.out.println("Using System.out.println")
System.out.print("Using System.out.print - ")
System.out.println("same line")
System.out.printf("Formatted: %s is %d years old%n", "Alice", 30)
// System.out is a PrintStream, so you can use all PrintStream methods
System.out.append('A' as char)
System.out.append('B' as char)
System.out.append('C' as char)
System.out.println()
// Checking what System.out actually is
println "System.out class: ${System.out.getClass().name}"
// Using the write method for raw bytes
System.out.write("Raw bytes\n".getBytes())
// Printing without the Groovy wrapper
def out = System.out
out.println("Direct reference to PrintStream")
Output
Using System.out.println Using System.out.print - same line Formatted: Alice is 30 years old ABC System.out class: java.io.PrintStream Raw bytes Direct reference to PrintStream
When would you use System.out directly? Mainly when you need to pass the output stream as a parameter to another method, when you need raw byte-level writing, or when you need to flush the stream explicitly.
Example 6: Writing to System.err
System.err is the standard error stream. It is a completely separate stream from System.out, which means error messages can be captured or redirected independently. This is important for scripts that pipe output between commands.
Example 6: System.err for Error Output
// Basic error output
System.err.println("ERROR: Something went wrong!")
// Using System.err for warnings
System.err.println("WARNING: Deprecated method called")
// Mixing stdout and stderr
println "Processing file: data.csv"
System.err.println("WARNING: File is larger than expected")
println "Processing complete: 1000 rows"
System.err.println("ERROR: 5 rows had invalid data")
// A helper method for error output
def error(String msg) {
System.err.println("[ERROR] ${new Date().format('HH:mm:ss')} - ${msg}")
}
def warn(String msg) {
System.err.println("[WARN] ${new Date().format('HH:mm:ss')} - ${msg}")
}
// Using the helpers
error("Database connection failed")
warn("Retrying in 5 seconds")
println "Normal output continues here"
Output
Processing file: data.csv Processing complete: 1000 rows Normal output continues here
The output above shows only stdout. The System.err lines go to stderr, which is a separate stream. In a terminal, both stdout and stderr appear together by default. But when redirecting output, you can separate them: groovy script.groovy > output.log 2> errors.log. This is why using System.err for error messages matters.
Example 7: Printing Lists and Collections
Groovy’s println produces clean, readable output for collections out of the box. No need for Arrays.toString() like in Java.
Example 7: Printing Lists and Collections
// Lists print with square brackets
def fruits = ['Apple', 'Banana', 'Cherry']
println fruits
// Ranges
println(1..10)
// Sets
def uniqueNums = [1, 2, 3, 2, 1] as Set
println uniqueNums
// Nested lists
def matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
println matrix
// Pretty printing each element
println "--- Individual items ---"
fruits.each { println " - ${it}" }
// Joining elements for custom formatting
println fruits.join(", ")
println fruits.join(" | ")
// Printing with index
fruits.eachWithIndex { item, index ->
println "${index + 1}. ${item}"
}
// Formatted list output
def scores = [95, 88, 72, 91, 85]
println "\nScores: ${scores}"
println "Count: ${scores.size()}"
println "Sum: ${scores.sum()}"
println "Avg: ${scores.average()}"
println "Max: ${scores.max()}"
println "Min: ${scores.min()}"
Output
[Apple, Banana, Cherry] 1..10 [1, 2, 3] [[1, 2, 3], [4, 5, 6], [7, 8, 9]] --- Individual items --- - Apple - Banana - Cherry Apple, Banana, Cherry Apple | Banana | Cherry 1. Apple 2. Banana 3. Cherry Scores: [95, 88, 72, 91, 85] Count: 5 Sum: 431 Avg: 86.2 Max: 95 Min: 72
Example 8: Printing Maps
Maps get equally clean output. Groovy displays them as [key:value] pairs, which is much nicer than Java’s default map output.
Example 8: Printing Maps
// Simple map
def person = [name: 'Alice', age: 30, city: 'London']
println person
// Nested maps
def config = [
database: [host: 'localhost', port: 5432],
cache: [enabled: true, ttl: 300]
]
println config
// Pretty printing a map
println "\n--- User Details ---"
person.each { key, value ->
printf " %-8s: %s%n", key, value
}
// Printing map as a table
def users = [
[name: 'Alice', age: 30, role: 'Admin'],
[name: 'Bob', age: 25, role: 'User'],
[name: 'Charlie', age: 35, role: 'Editor']
]
printf "%n%-10s %-5s %-8s%n", "Name", "Age", "Role"
printf "%-10s %-5s %-8s%n", "----", "---", "----"
users.each { user ->
printf "%-10s %-5d %-8s%n", user.name, user.age, user.role
}
Output
[name:Alice, age:30, city:London] [database:[host:localhost, port:5432], cache:[enabled:true, ttl:300]] --- User Details --- name : Alice age : 30 city : London Name Age Role ---- --- ---- Alice 30 Admin Bob 25 User Charlie 35 Editor
Combining printf with maps lets you build formatted table output very easily. This is great for CLI tools and report scripts.
Example 9: sprintf – Format Without Printing
While printf formats and prints in one step, sprintf formats and returns a String without printing it. This is useful when you need the formatted string for further processing, logging, or storing.
Example 9: sprintf for String Formatting
// sprintf returns a formatted string
def formatted = sprintf("Hello, %s! You are %d years old.", "Alice", 30)
println formatted
// Currency formatting
def price = 1299.99
def currency = sprintf("\$%,.2f", price)
println "Price: ${currency}"
// Building formatted log entries
def logEntry(String level, String msg) {
sprintf("[%-5s] %s - %s", level, new Date().format('yyyy-MM-dd HH:mm:ss'), msg)
}
def logs = []
logs << logEntry("INFO", "Application started")
logs << logEntry("DEBUG", "Loading configuration")
logs << logEntry("WARN", "Cache miss for key: user_42")
logs << logEntry("ERROR", "Connection timeout after 30s")
logs.each { println it }
// Padding and alignment
println "\n--- Product Report ---"
def products = [
[name: 'Widget A', qty: 150, price: 9.99],
[name: 'Gadget B', qty: 42, price: 29.95],
[name: 'Doohickey C', qty: 1200, price: 4.50]
]
println sprintf("%-15s %8s %10s %12s", "Product", "Qty", "Price", "Total")
println "-" * 47
products.each { p ->
def total = p.qty * p.price
println sprintf("%-15s %8d %10.2f %12.2f", p.name, p.qty, p.price, total)
}
Output
Hello, Alice! You are 30 years old. Price: $1,299.99 [INFO ] 2026-07-02 22:08:01 - Application started [DEBUG] 2026-07-02 22:08:01 - Loading configuration [WARN ] 2026-07-02 22:08:01 - Cache miss for key: user_42 [ERROR] 2026-07-02 22:08:01 - Connection timeout after 30s --- Product Report --- Product Qty Price Total ----------------------------------------------- Widget A 150 9.99 1498.50 Gadget B 42 29.95 1257.90 Doohickey C 1200 4.50 5400.00
Think of sprintf as the “quiet” version of printf. Same formatting power, but it hands you back a String instead of writing to the console. The timestamps in the log entries come from new Date(), so you will see your own current date and time when you run this.
Example 10: Print with Flush
Output in Groovy is buffered by default. When you use print (without newline), the text might not appear immediately on the console because the buffer has not been flushed yet. Calling System.out.flush() forces the buffer to be written.
Example 10: Flushing Output
// Without flush, print output may be buffered
print "Processing"
System.out.flush() // Force the text to appear now
// Simulate a progress indicator
(1..5).each { step ->
print "."
System.out.flush() // Each dot appears immediately
Thread.sleep(100) // Simulate work
}
println " Done!"
// Auto-flush PrintStream
def autoFlushOut = new PrintStream(System.out, true) // true = autoFlush
autoFlushOut.print("This flushes automatically")
autoFlushOut.println()
// A helper for immediate print
def printNow(String text) {
print text
System.out.flush()
}
printNow("Step 1: Connecting... ")
Thread.sleep(100)
println "OK"
printNow("Step 2: Loading data... ")
Thread.sleep(100)
println "OK"
printNow("Step 3: Processing... ")
Thread.sleep(100)
println "OK"
Output
Processing..... Done! This flushes automatically Step 1: Connecting... OK Step 2: Loading data... OK Step 3: Processing... OK
Flushing matters most in long-running scripts where you want real-time feedback. Without flushing, your progress dots might all appear at once at the end rather than one by one.
Example 11: Redirecting Output to a File
Sometimes you want to capture println output and redirect it to a file or a string. You can do this by temporarily swapping System.out.
Example 11: Redirecting Output
// Capture println output to a StringWriter
def captureOutput(Closure code) {
def originalOut = System.out
def baos = new ByteArrayOutputStream()
System.out = new PrintStream(baos)
try {
code()
} finally {
System.out = originalOut
}
return baos.toString().trim()
}
def captured = captureOutput {
println "This was captured!"
println "So was this."
println "Line 3 of captured output."
}
println "Captured output:"
println "---"
println captured
println "---"
// Redirect to a file
def logFile = File.createTempFile("groovy-output-", ".log")
def originalOut = System.out
System.out = new PrintStream(new FileOutputStream(logFile))
println "This goes to the file"
println "So does this"
println "Log entry at ${new Date()}"
System.out = originalOut // Restore console output
println "Back to console now"
println "File contents:"
println logFile.text
// Cleanup
logFile.delete()
Output
Captured output: --- This was captured! So was this. Line 3 of captured output. --- Back to console now File contents: This goes to the file So does this Log entry at Thu Jul 02 22:08:03 SGT 2026
This pattern is useful for unit testing (capturing output to verify it) and for creating simple logging without a framework. Always restore the original System.out in a finally block to avoid losing console output. The last log line uses new Date(), so it will show the date, time, and timezone of whenever you run the script.
Example 12: print vs Logging Frameworks
For quick scripts and prototyping, println is fine. But for production applications, you should use a proper logging framework. Here is the difference and when to switch.
Example 12: println vs Logging
import java.util.logging.Logger
import java.util.logging.Level
// ---- Using println (quick and dirty) ----
println "[INFO] Application started"
println "[DEBUG] User count: 42"
println "[ERROR] Failed to connect to DB"
// ---- Using java.util.logging (built into JDK) ----
def log = Logger.getLogger("MyApp")
log.info("Application started")
log.fine("User count: 42")
log.severe("Failed to connect to DB")
// ---- Groovy's built-in @Log annotations ----
// In a class file, you can use:
// @groovy.util.logging.Log (java.util.logging)
// @groovy.util.logging.Slf4j (SLF4J)
// @groovy.util.logging.Log4j2 (Log4j2)
// When to use println:
// - Quick scripts and one-off utilities
// - Debugging during development
// - Learning and prototyping
// - Scripts with no logging framework available
// When to use a logging framework:
// - Production applications
// - When you need log levels (DEBUG, INFO, WARN, ERROR)
// - When you need log rotation and file output
// - When different components need different log levels
println "\n--- Comparison ---"
println "println: Simple, always visible, no configuration"
println "logging: Configurable levels, output targets, formatting"
Output
[INFO] Application started [DEBUG] User count: 42 [ERROR] Failed to connect to DB --- Comparison --- println: Simple, always visible, no configuration logging: Configurable levels, output targets, formatting
Notice that log.fine() (equivalent to DEBUG) does not produce output by default because the default log level is INFO. This is the advantage of logging frameworks – you control what gets output without changing code. Also note that the log.info() and log.severe() records are missing from the stdout capture above: java.util.logging writes to stderr by default, printing each record with a timestamp header line.
Example 13: Real-World – Building Console Apps
Here is a practical example showing how to combine all the output methods to build a clean console application.
Example 13: Console App Output Patterns
// Helper methods for clean console output
def banner(String text) {
def line = "=" * (text.length() + 4)
println line
println "| ${text} |"
println line
}
def section(String title) {
println "\n--- ${title} ---"
}
def status(String task, String result) {
printf " %-30s [%s]%n", task, result
}
def progressBar(int current, int total, int width = 30) {
def percent = (current / total * 100) as int
def filled = (current / total * width) as int
def empty = width - filled
def bar = "${'#' * filled}${'-' * empty}"
print "\r [${bar}] ${percent}%"
System.out.flush()
if (current == total) println()
}
// Use the helpers
banner("System Health Check")
section("Services")
status("Database connection", "OK")
status("Cache server", "OK")
status("Message queue", "WARN")
status("External API", "FAIL")
section("Processing Files")
def totalFiles = 10
(1..totalFiles).each { i ->
progressBar(i, totalFiles)
Thread.sleep(50)
}
section("Summary")
println " Total checks: 4"
println " Passed: 2, Warnings: 1, Failed: 1"
printf " Uptime: %.1f%%%n", 75.0
Output
======================= | System Health Check | ======================= --- Services --- Database connection [OK] Cache server [OK] Message queue [WARN] External API [FAIL] --- Processing Files --- [##############################] 100% --- Summary --- Total checks: 4 Passed: 2, Warnings: 1, Failed: 1 Uptime: 75.0%
Notice the use of \r (carriage return) in the progress bar. This moves the cursor back to the beginning of the line without advancing to a new line, so each update overwrites the previous one. Combined with flush(), this creates a smooth progress animation.
Example 14: Real-World – Debug Output Patterns
During development, you often need quick debug output. Here are some patterns that make debugging with println more effective.
Example 14: Debug Output Patterns
// Variable inspection
def name = "Alice"
def age = 30
def scores = [95, 88, 92]
// Quick debug dump
println "DEBUG: name=${name}, age=${age}, scores=${scores}"
// Labeled output for clarity
println ">>> name.class = ${name.getClass().simpleName}"
println ">>> scores.size = ${scores.size()}"
println ">>> scores.sum = ${scores.sum()}"
// Object dump helper
def dump(String label, Object obj) {
println "=== ${label} ==="
if (obj instanceof Map) {
obj.each { k, v -> println " ${k}: ${v} (${v?.getClass()?.simpleName})" }
} else if (obj instanceof Collection) {
obj.eachWithIndex { item, i -> println " [${i}]: ${item}" }
} else {
println " value: ${obj}"
println " type: ${obj?.getClass()?.name}"
}
println "=== end ${label} ===\n"
}
// Using the dump helper
def user = [name: 'Bob', age: 25, active: true]
dump("user", user)
dump("scores", scores)
dump("single value", 42)
// Conditional debug output
def DEBUG = true
def debug = { String msg ->
if (DEBUG) System.err.println("[DBG] ${msg}")
}
debug("Entering method processData()")
println "Processing data..."
debug("Data processed successfully, 100 records")
println "Done."
Output
DEBUG: name=Alice, age=30, scores=[95, 88, 92] >>> name.class = String >>> scores.size = 3 >>> scores.sum = 275 === user === name: Bob (String) age: 25 (Integer) active: true (Boolean) === end user === === scores === [0]: 95 [1]: 88 [2]: 92 === end scores === === single value === value: 42 type: java.lang.Integer === end single value === Processing data... Done.
Sending debug output to System.err is a smart pattern. Your normal program output goes to stdout and debug noise goes to stderr. That is why the two [DBG] lines do not appear in the Output block above, which shows stdout only. When you run the script, both appear in the terminal. But when you pipe the output to a file or another command, only the clean program output goes through.
printf and Formatted Output
Let us go deeper into printf formatting, for the cases where plain groovy println output is not aligned or precise enough. The format specifiers follow the Java Formatter specification and give you precise control over how values are displayed.
Common Format Specifiers:
| Specifier | Type | Example | Output |
|---|---|---|---|
%s | String | printf "%s", "hi" | hi |
%d | Integer | printf "%d", 42 | 42 |
%f | Float | printf "%.2f", 3.14 | 3.14 |
%b | Boolean | printf "%b", true | true |
%x | Hex | printf "%x", 255 | ff |
%o | Octal | printf "%o", 8 | 10 |
%e | Scientific | printf "%e", 1234.5 | 1.234500e+03 |
%n | Newline | printf "hi%n" | hi + newline |
You can also use Groovy’s String.format() method, which works identically to sprintf:
String.format() vs sprintf
// Both do the same thing
def result1 = sprintf("Price: \$%.2f", 19.99)
def result2 = String.format("Price: \$%.2f", 19.99)
println result1
println result2
println "Same? ${result1 == result2}"
Output
Price: $19.99 Price: $19.99 Same? true
System.out and System.err
Understanding the relationship between Groovy’s print/println and System.out is important. Groovy’s output methods are essentially wrappers around System.out. When you call println "Hello" in a Groovy script, it delegates to System.out.println("Hello"). In other words, every groovy println call ends up on stdout.
The two output streams available are:
- System.out (stdout) – Standard output, used by
printandprintln - System.err (stderr) – Standard error, must be called explicitly
In a terminal, both streams appear in the same console. But they can be redirected independently using shell redirection (> for stdout, 2> for stderr). This is why using System.err for error messages is a best practice in command-line tools.
Printing Collections and Maps
One area where Groovy really shines over Java is how it prints collections. In Java, printing an array gives you something like [Ljava.lang.String;@1a2b3c, which is useless. Groovy always calls a meaningful toString() and gives you the actual contents, which is why a quick groovy println of a list or map is often all the debugging you need.
Collection Printing Comparison
// Arrays print their contents in Groovy (unlike Java!)
String[] javaArray = ["one", "two", "three"] as String[]
println "Array: ${javaArray}"
// Even arrays of arrays
int[][] grid = [[1, 2], [3, 4]] as int[][]
println "Grid: ${grid}"
// LinkedHashMap preserves insertion order
def orderedMap = [:] as LinkedHashMap
orderedMap.put("first", 1)
orderedMap.put("second", 2)
orderedMap.put("third", 3)
println "Ordered: ${orderedMap}"
// TreeMap sorts by key
def sortedMap = new TreeMap([c: 3, a: 1, b: 2])
println "Sorted: ${sortedMap}"
// Custom toString in your classes
class Product {
String name
double price
String toString() { "${name} (\$${price})" }
}
def products = [new Product(name: 'Laptop', price: 999.99),
new Product(name: 'Mouse', price: 29.99)]
println "Products: ${products}"
Output
Array: [one, two, three] Grid: [[1, 2], [3, 4]] Ordered: [first:1, second:2, third:3] Sorted: [a:1, b:2, c:3] Products: [Laptop ($999.99), Mouse ($29.99)]
This is one of those Groovy niceties that you start to take for granted. Overriding toString() in your classes gives you clean output everywhere your objects are printed.
Print with Flush and Redirecting Output
We covered flushing in Example 10 and redirecting in Example 11. Here is what matters:
- Flush when using
print(no newline) and you need immediate output – especially for progress indicators - Redirect by swapping
System.outwith a newPrintStream– always restore it in afinallyblock printlntypically auto-flushes because it writes a newline, but this is not guaranteed by the spec- You can create an auto-flushing
PrintStreamwithnew PrintStream(outputStream, true)
sprintf – Format Without Printing
We saw sprintf in Example 9. To recap: it uses the same format specifiers as printf but returns a String instead of printing to the console. This makes it perfect for building formatted strings that you will use later – in log files, database records, API responses, or any context where you need the formatted text as data rather than immediate console output.
print vs Logging Frameworks
A common question is: when should I stop scattering groovy println calls and switch to a logging framework? Here is a simple rule of thumb:
- Use println for scripts, prototypes, one-off utilities, learning, and quick debugging
- Use a logging framework when your code will be part of a larger application, needs configurable log levels, requires log rotation, or needs to output to multiple destinations (file + console + remote server)
Groovy makes logging frameworks easy with built-in AST transformations: @groovy.util.logging.Log, @groovy.util.logging.Slf4j, @groovy.util.logging.Log4j2. Just annotate your class and a log field is automatically available. Check the Groovy logging documentation for details.
Real-World Use Cases
Let us tie everything together with two practical scenarios that combine multiple output techniques.
CSV Report Generator
CSV Report Generator with Mixed Output
def employees = [
[name: 'Alice Johnson', dept: 'Engineering', salary: 95000],
[name: 'Bob Smith', dept: 'Marketing', salary: 72000],
[name: 'Charlie Brown', dept: 'Engineering', salary: 88000],
[name: 'Diana Ross', dept: 'Sales', salary: 67000],
[name: 'Eve Wilson', dept: 'Engineering', salary: 102000]
]
// Console report (stdout)
println "=" * 55
printf "%-18s %-14s %12s%n", "Name", "Department", "Salary"
println "=" * 55
employees.each { emp ->
printf "%-18s %-14s %,12d%n", emp.name, emp.dept, emp.salary
}
println "-" * 55
// Summary stats
def totalSalary = employees.sum { it.salary }
def avgSalary = totalSalary / employees.size()
printf "%-18s %-14s %,12d%n", "TOTAL", "", totalSalary
printf "%-18s %-14s %,12.0f%n", "AVERAGE", "", avgSalary
// Department breakdown (stderr for diagnostic info)
System.err.println("\n[INFO] Department breakdown:")
employees.groupBy { it.dept }.each { dept, emps ->
System.err.printf("[INFO] %-14s: %d employees, avg salary: \$%,.0f%n",
dept, emps.size(), emps.average { it.salary })
}
Output
======================================================= Name Department Salary ======================================================= Alice Johnson Engineering 95,000 Bob Smith Marketing 72,000 Charlie Brown Engineering 88,000 Diana Ross Sales 67,000 Eve Wilson Engineering 102,000 ------------------------------------------------------- TOTAL 424,000 AVERAGE 84,800 [INFO] Department breakdown: [INFO] Engineering : 3 employees, avg salary: $95,000 [INFO] Marketing : 1 employees, avg salary: $72,000 [INFO] Sales : 1 employees, avg salary: $67,000
Interactive Menu System
Interactive Menu Output Pattern
def showMenu() {
println "\n╔══════════════════════════════╗"
println "║ Groovy Task Manager ║"
println "╠══════════════════════════════╣"
println "║ 1. List all tasks ║"
println "║ 2. Add new task ║"
println "║ 3. Complete a task ║"
println "║ 4. Show statistics ║"
println "║ 5. Exit ║"
println "╚══════════════════════════════╝"
print " Enter your choice (1-5): "
System.out.flush()
}
def showStats(List tasks) {
def total = tasks.size()
def done = tasks.count { it.done }
def pending = total - done
println "\n 📊 Task Statistics"
printf " Total: %d%n", total
printf " Completed: %d (%.0f%%)%n", done, total ? (done / total * 100) : 0
printf " Pending: %d (%.0f%%)%n", pending, total ? (pending / total * 100) : 0
// Simple bar chart
if (total > 0) {
def barWidth = 20
def filledWidth = (done / total * barWidth) as int
def bar = "${'█' * filledWidth}${'░' * (barWidth - filledWidth)}"
println " Progress: [${bar}]"
}
}
// Demo the menu
showMenu()
println "3" // Simulating user input
// Demo the stats
def sampleTasks = [
[name: "Write blog post", done: true],
[name: "Review PR", done: true],
[name: "Fix bug #42", done: false],
[name: "Update docs", done: false],
[name: "Deploy v2.0", done: false]
]
showStats(sampleTasks)
Output
╔══════════════════════════════╗ ║ Groovy Task Manager ║ ╠══════════════════════════════╣ ║ 1. List all tasks ║ ║ 2. Add new task ║ ║ 3. Complete a task ║ ║ 4. Show statistics ║ ║ 5. Exit ║ ╚══════════════════════════════╝ Enter your choice (1-5): 3 📊 Task Statistics Total: 5 Completed: 2 (40%) Pending: 3 (60%) Progress: [████████░░░░░░░░░░░░]
Best Practices
Here are the guidelines for using output methods effectively in Groovy:
- Use
printlnfor general-purpose output – it is the default choice for most situations - Use
printwhen building output on a single line – progress bars, inline status updates, prompts - Use
printffor aligned tables, formatted numbers, and fixed-width output - Use
sprintfwhen you need the formatted string as a value, not as console output - Use
System.errfor error messages and diagnostics – keeps them separate from program output - Flush after
printwhen you need immediate output – especially in progress indicators - Prefer GString interpolation over concatenation or printf for simple messages
- Switch to logging frameworks for production applications – Groovy’s
@Logannotations make this easy - Override
toString()in your classes for meaningful output when objects are printed
Conclusion
Groovy gives you a rich set of output methods that go well beyond the basic groovy println statement. We covered 14 practical examples showing print, println, printf, sprintf, System.out, and System.err in action.
The key differences are simple: print keeps you on the same line, println adds a newline, printf gives you formatted control, and System.err sends output to the error stream. Combine these with GString interpolation and you have a solid toolkit for clean, professional console output.
Next up in the series, we will explore Groovy sleep and timing – how to pause execution, measure elapsed time, and build timeout patterns in your scripts.
Summary
printlnadds a newline,printdoes notprintfgives you C-style formatting with%s,%d,%fspecifierssprintfformats and returns a String without printing- Use
System.errfor error messages to keep them separate from program output - Call
System.out.flush()afterprintwhen you need immediate output - GString interpolation (
"Hello ${name}") is the preferred way to build output strings
If you also work with build tools, CI/CD pipelines, or cloud CLIs, check out Command Playground to practice 105+ CLI tools directly in your browser — no install needed.
Up next: Groovy Sleep and Timing – Pause Execution
Frequently Asked Questions
What is the difference between print and println in Groovy?
The only difference is that println adds a newline character at the end of the output, while print does not. Both methods call toString() on the argument and write to System.out. Use println for line-by-line output and print when you want to build a line piece by piece, such as progress indicators.
How do I use printf in Groovy?
Groovy’s printf works like C’s printf and Java’s System.out.printf. Use format specifiers like %s for strings, %d for integers, %f for floats, and %n for newline. Example: printf ‘Name: %s, Age: %d%n’, ‘Alice’, 30. Unlike println, printf does not add a newline automatically.
What is sprintf in Groovy?
sprintf is like printf but instead of printing to the console, it returns a formatted String. This is useful when you need the formatted text as data – for logging, storing in a variable, or passing to another method. Example: def msg = sprintf('Score: %.2f%%', 95.5) creates ‘Score: 95.50%’ without printing it.
How do I print to stderr in Groovy?
Use System.err.println() or System.err.print() to write to the standard error stream. For example: System.err.println('ERROR: File not found'). This is important for CLI scripts because stderr can be redirected separately from stdout using shell redirection (2> errors.log).
Should I use println or a logging framework in Groovy?
A quick groovy println is fine for scripts, prototyping, learning, and debugging. Switch to a logging framework (like SLF4J or Log4j2) for production applications that need configurable log levels, log rotation, or multiple output destinations. Groovy makes logging easy with annotations like @groovy.util.logging.Slf4j which automatically add a log field to your class.
Interview Questions on Groovy Output Methods
“Scenario questions, not trivia: this is the form this topic takes in a real interview.”
Q: A script prints a progress spinner with print '.' inside a loop, but nothing shows up until the whole job finishes. What is going on?
Buffering. System.out is a PrintStream that auto-flushes on println, but a bare print with no newline can sit in the buffer, especially when stdout is redirected to a file or pipe rather than an interactive terminal. The fix is to call System.out.flush() after each print so the dots appear as work happens. The gotcha an interviewer wants to hear: the same script behaves differently in a terminal versus a Jenkins log, because buffering policy depends on where stdout points.
Q: In a Jenkins console log, a teammate’s error messages appear out of order relative to the normal output around them. Why, and does it matter?
Because println writes to stdout and System.err.println writes to stderr, and those are two independently buffered streams. When a tool like Jenkins merges them into one log, flush timing decides the interleaving, so relative order between the streams is not guaranteed. It matters for debugging: an error can appear lines away from the output that triggered it. If exact ordering matters, write both messages to the same stream, or better, use a logging framework that timestamps every line.
Q: A report generator uses printf with \n and looks fine on the Linux build agents, but the file it produces is flagged as one giant line by a Windows tool. What would you change?
Swap the hard-coded \n for %n in the format string. \n is always a bare line feed, while %n expands to the platform line separator, carriage return plus line feed on Windows. Notepad-era tools and some CSV readers on Windows genuinely care. The nuance worth adding: for files that must be byte-identical across platforms, like golden test fixtures, you want the opposite choice, a fixed \n, so the output does not depend on which OS ran the build.
Q: You need to write a Spock test around a legacy Groovy script that reports everything through println. How do you capture its output?
Two options. The general JVM one: swap the stream with System.setOut(new PrintStream(buffer)), run the script, restore the original stream in cleanup, then assert on the buffer’s text. The Groovy-specific one interviewers like: a script’s println goes through groovy.lang.Script, and if the binding contains an out variable, output is routed there instead of System.out. So running it via GroovyShell with a binding that sets out to a StringWriter captures everything without touching global state, which keeps parallel tests safe.
Q: A Gradle task that dumps thousands of records with one groovy println per record is dramatically slower than writing the same data to a file. Why?
Every println on System.out is a synchronized call, and with console auto-flush each line can trigger its own I/O operation, so per-line cost dominates once you print thousands of lines. Batching fixes it: build the output with a StringBuilder or collect().join('\n') and print once, or write through a BufferedWriter. The follow-up point that lands well: console output is often the slowest sink in the whole task, terminals render slower than disks accept writes, which is why redirecting the same job to a file already speeds it up.
Related Posts
Previous in Series: Groovy Copy File – All Methods Explained
Next in Series: Groovy Sleep and Timing – Pause Execution
Related Topics You Might Like:
- Groovy Hello World – Your First Program
- Groovy String Tutorial – The Complete Guide
- Groovy File Read, Write, and Delete
This post is part of the Groovy & Grails Cookbook series on TechnoScripts.com

No comment