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.x.
“The first thing every programmer learns is how to print. The last thing they master is knowing what to print and where.”
Brian Kernighan & Dennis Ritchie, The C Programming Language
Last Updated: March 2026 | Tested on: Groovy 5.x, 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.
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 |
Syntax and Basic Usage
All output methods in Groovy are simple. You do not need parentheses with println 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-03-08 14:30:00 - Application started [DEBUG] 2026-03-08 14:30:00 - Loading configuration [WARN ] 2026-03-08 14:30:00 - Cache miss for key: user_42 [ERROR] 2026-03-08 14:30:00 - 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.
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 Sun Mar 08 14:30:00 UTC 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.
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.
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. 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. 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").
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.
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 using println 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 println. 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?
Use println for quick 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.
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