Groovy print vs println – All Output Methods Explained with 14 Examples

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:

terminal (default)greater-than out.log2 greater-than err.logprintln, print, printfSystem.out(stdout)System.err.printlnSystem.err(stderr)Terminal orredirect?Both mixedon consoleout.log(stdout only)err.log(stderr only)Groovy Output Streams: stdout vs stderr Routing and Redirection

Tip: click the diagram to open it full screen, then use the zoom and pan controls for a closer look.

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.

MethodNewlineFormattingReturnsUse Case
printNoCalls toString()voidInline output, progress indicators
printlnYesCalls toString()voidStandard line-by-line output
printfNoFormat string (%s, %d, etc.)voidFormatted/aligned output
System.out.printNoCalls toString()voidWhen you need the Java PrintStream
System.errDependsCalls toString()voidError messages, separate stream
sprintfNoFormat stringStringFormat 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:

SpecifierTypeExampleOutput
%sStringprintf "%s", "hi"hi
%dIntegerprintf "%d", 4242
%fFloatprintf "%.2f", 3.143.14
%bBooleanprintf "%b", truetrue
%xHexprintf "%x", 255ff
%oOctalprintf "%o", 810
%eScientificprintf "%e", 1234.51.234500e+03
%nNewlineprintf "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 print and println
  • 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.

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.out with a new PrintStream – always restore it in a finally block
  • println typically auto-flushes because it writes a newline, but this is not guaranteed by the spec
  • You can create an auto-flushing PrintStream with new 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.

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 println for general-purpose output – it is the default choice for most situations
  • Use print when building output on a single line – progress bars, inline status updates, prompts
  • Use printf for aligned tables, formatted numbers, and fixed-width output
  • Use sprintf when you need the formatted string as a value, not as console output
  • Use System.err for error messages and diagnostics – keeps them separate from program output
  • Flush after print when 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 @Log annotations 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

  • println adds a newline, print does not
  • printf gives you C-style formatting with %s, %d, %f specifiers
  • sprintf formats and returns a String without printing
  • Use System.err for error messages to keep them separate from program output
  • Call System.out.flush() after print when 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.

Previous in Series: Groovy Copy File – All Methods Explained

Next in Series: Groovy Sleep and Timing – Pause Execution

Related Topics You Might Like:

This post is part of the Groovy & Grails Cookbook series on TechnoScripts.com

RahulAuthor posts

Avatar for Rahul

Rahul is a passionate IT professional who loves to sharing his knowledge with others and inspiring them to expand their technical knowledge. Rahul's current objective is to write informative and easy-to-understand articles to help people avoid day-to-day technical issues altogether. Follow Rahul's blog to stay informed on the latest trends in IT and gain insights into how to tackle complex technical issues. Whether you're a beginner or an expert in the field, Rahul's articles are sure to leave you feeling inspired and informed.

No comment

Leave a Reply

Your email address will not be published. Required fields are marked *