In my project test suite there is big usage of
System.out.println
I'm trying to redirect these output to log file (through configuration or from single point without refactoring whole project ) so that can be disabled when necessary to improve performance. I'm using log4j for logging.
Does any one know is this possible ? if so how to do it ?
Thanks in advance.
Given that it's better replace the System.out.println(), sometimes we have no choice. Anyway I've made a little utility for that:
SystemOutToSlf4j.enableForClass(MyClass.class)
Then all the println originated from MyClass will be redirected to the logger.
See this post for more details...
public class SystemOutToSlf4j extends PrintStream {
private static final PrintStream originalSystemOut = System.out;
private static SystemOutToSlf4j systemOutToLogger;
/**
* Enable forwarding System.out.println calls to the logger if the stacktrace contains the class parameter
* #param clazz
*/
public static void enableForClass(Class clazz) {
systemOutToLogger = new SystemOutToSlf4j(originalSystemOut, clazz.getName());
System.setOut(systemOutToLogger);
}
/**
* Enable forwarding System.out.println calls to the logger if the stacktrace contains the package parameter
* #param packageToLog
*/
public static void enableForPackage(String packageToLog) {
systemOutToLogger = new SystemOutToSlf4j(originalSystemOut, packageToLog);
System.setOut(systemOutToLogger);
}
/**
* Disable forwarding to the logger resetting the standard output to the console
*/
public static void disable() {
System.setOut(originalSystemOut);
systemOutToLogger = null;
}
private String packageOrClassToLog;
private SystemOutToSlf4j(PrintStream original, String packageOrClassToLog) {
super(original);
this.packageOrClassToLog = packageOrClassToLog;
}
#Override
public void println(String line) {
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
StackTraceElement caller = findCallerToLog(stack);
if (caller == null) {
super.println(line);
return;
}
org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(caller.getClass());
log.info(line);
}
public StackTraceElement findCallerToLog(StackTraceElement[] stack) {
for (StackTraceElement element : stack) {
if (element.getClassName().startsWith(packageOrClassToLog))
return element;
}
return null;
}
}
My suggestion would be to refactor if possible.
For a possible solution, check these similar questions
log4j redirect stdout to DailyRollingFileAppender
Redirect System.out.println to Log4J, while keeping class name information
I think you can use System.setOut(PrintStream) to set your output to a file output stream. Then you can put this line in your BeforeClass method. I like to use a BaseTest class and put this line of code in the beforeclass method of that class. Then make all test cases extend this cclass.
Use shell redirection. Figure out the "java" invocation for your project, if you're on most vaguely UNIX-like systems, ps aux | grep java will help.
Then just run this command with > /path/to/logfile. Example:
java -jar myjar.jar -cp path/to/lib.jar:path/to/otherlib.jar com.bigcorp.myproject.Main > /var/log/myproject.log
public class RecursiveLogging {
public static void main(String[] args) {
System.setOut(new PrintStream(new CustomOutputStream()));
TestMyException.testPrint();
}
}
class CustomOutputStream extends OutputStream {
private Logger logger = Logger.getLogger(this.getClass());
#Override
public final void write(int b) throws IOException {
// the correct way of doing this would be using a buffer
// to store characters until a newline is encountered,
// this implementation is for illustration only
logger.info((char) b);
}
#Override
public void write(byte[] b, int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
byte[] pb = new byte[len];
for (int i = 0 ; i < len ; i++) {
pb[i] = (b[off + i]);
}
String str = new String(pb);
logger.info(str);
}
}
My solution is pretty simple and supports all PrintStream functionality without overloading everything. overloading only flush() as it called by PrintStream methods every new line.
public class ConsoleToLogger
{
private Logger log;
private PrintStream originalStream;
private Level logLevel;
private ByteArrayBufferOutStream buffer;
private PrintStream bufferPrintStream;
ConsoleToLogger(PrintStream realPrintStream, Level pLogLevel)
{
buffer = new ByteArrayBufferOutStream();
bufferPrintStream = new PrintStream(buffer);
originalStream = realPrintStream;
logLevel = pLogLevel;
log = Logger.getLogger(Level.ERROR.equals(pLogLevel) ? "STDERR" : "STDOUT");
}
public PrintStream getPrintStream()
{
return bufferPrintStream;
}
private class ByteArrayBufferOutStream
extends ByteArrayOutputStream
{
#Override
public void flush()
throws IOException
{
super.flush();
String message = buffer.toString();
originalStream.println(message);
log.log(logLevel, message);
buffer.reset();
}
}
}
// assign to System.out and system.err
System.setOut(new ConsoleToLogger(System.out, Level.INFO).getPrintStream());
System.setErr(new ConsoleToLogger(System.err, Level.ERROR).getPrintStream());
Related
I'm writing an Undo Stack and I'm having trouble reading the file and invoking the methods written with in the txt file. I've looked at other post but they don't seem to provide a clear answer. I'm using the algs4 library and the In class to read the file and the In class to read the file. Problems only start once I'm in the main method.
Overall Code
package edu.princeton.cs.algs4;
import java.util.NoSuchElementException;
import java.util.Scanner;
/**
*
*
*
* #author James Bond
*/
public class Undo {
private Stack<Character> undo;
private Stack<Character> redo;
private Stack<Character> reversed;
/**
*
* #param write: This method uses while loop to empty all the elements from
* the stack.
* #param write: It also uses the push and pop method to put and remove
* items from the stack.
* #param ch: The character variable ch stores input from the user.
*/
public void write(Character ch) {
undo.push(ch);
while (redo.isEmpty() != true) {
redo.pop();
}
}
public void Undo() {
if (undo.isEmpty()) {
throw new NoSuchElementException("Stack Underflow");
}
char poppedUndoChar = undo.pop();
redo.push(poppedUndoChar);
}
/**
* #param redo: Uses and if statement to check if the Stack is empty. and
* throws and exeception if it is empty. It also pushes the popped redo item
* onto thee the undo stack.
*/
public void redo() {
if (redo.isEmpty()) {
throw new NoSuchElementException("Stack Underflow");
}
char poppedRedoChar = redo.pop();
undo.push(poppedRedoChar);
}
private void query(String str) {
int n = str.length();
if (str.equals("undo")) {
Undo();
} else if ("redo".equals(str)) {
redo();
} else if ("write".equals(str)) {
write(str.charAt(n - 1));
} else if ("read".equals(str)) {
read();
} else if ("clear".equals(str)) {
clear();
}
}
public void read() {
while (undo.isEmpty() == false) {
reversed.push(undo.pop());
}
while (reversed.isEmpty() == false) {
System.out.println(reversed.pop());
}
}
public void clear() {
while (undo.isEmpty() == false) {
undo.pop();
}
while (redo.isEmpty() == false) {
redo.pop();
}
}
public void command(String str) {
if ((str instanceof String) == false) {
throw new IllegalArgumentException("Incorrect Input");
}
int n = str.length();
if (str.equals("write")) {
write(str.charAt(6));
} else if (str.equals("undo")) {
Undo();
} else if (str.equals("redo")) {
redo();
} else if (str.equals("clear")) {
clear();
} else if (str.equals("read")) {
read();
}
}
// Scanner input = new Scanner(str);
// Ssting out = input.nextLine();
// System.out.print(out);
//
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
In input = new In("C:\\Users\\James Bond\\input.txt");
String str = input.readAll();
Undo kgb = new Undo();
System.out.println(str);
while(input.readLine() != null){
kgb.command(str);
System.out.println(str);
}
}
}
Trouble Maker:
Specifically I need to read the file and invoke the methods.
The text in the file is as follows:
write c
write a
write r
write t
undo
undo
write t
read
clear
Which should produce this output:
cat
The source of the problem is my main method.
More context at: https://www.geeksforgeeks.org/implement-undo-and-redo-features-of-a-text-editor/
public static void main(String[] args) {
In input = new In("C:\\Users\\James Bond\\input.txt");
String str = input.readAll();
Undo kgb = new Undo();
System.out.println(str);
while(input.readLine() != null){
kgb.command(str);
System.out.println(str);
}
}
}
Any help would be greatly appreciated.
Fix your code by following steps:
You must to initialize your stacks, otherwise you will get NullPointerException. Add a constructor for your stacks like below:
public Undo() {
undo = new Stack<>();
redo = new Stack<>();
reversed = new Stack<>();
}
Modify command method:
replace str.equals("write") to str.startsWith("write")
Let's say I have simple code:
public static void Main(){
throw new NullPointerException("this is an npe");
}
How with slf4j do you make sure that that exception is logged to the log file. Is there a default setting in spring that you can set in application properties, or in the logback file, that makes sure this gets captured, even though it is not in the try catch?
Please note, I am NOT looking for a solution to a webcontroller, or how to use log.error(). I know how to do those. I am looking for an overarching setting so that we don't lose exceptions throughout the application. This can apply to OOM, or printing a heap dump etc. I am including spring as a tag just in case the solution is in the applications.properties, as this is a spring project.
Ok as you have said, you can add a controller advice:
#ControllerAdvice
public class YourCustomHandler extends ResponseEntityExceptionHandler {
#Autowired
MessageSource messageSource;
#Override
protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
// you can do logs here and pass a custom object that will parse to json object
return buildResponseEntity(apiError);
}
#ExceptionHandler(YourCustomException.class)
protected ResponseEntity<Object> handleTthException(
YourCustomException ex, Locale locale) {
return buildResponseEntity(apiError);
}
private ResponseEntity<Object> buildResponseEntity(ApiError apiError) {
return new ResponseEntity<>(apiError, apiError.getStatus());
}
}
Based off of this: https://sysgears.com/articles/how-to-redirect-stdout-and-stderr-writing-to-a-log4j-appender/, I have found something I believe works.
At beginning of psvm:
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
System.setErr(new PrintStream(new LoggingOutputStream(), true));
}
This says to print errors to my LoggingOutputStream.
Code for this class:
public class LoggingOutputStream extends OutputStream {
/**
* Default number of bytes in the buffer.
*/
private static final int DEFAULT_BUFFER_LENGTH = 2048;
/**
* Indicates stream state.
*/
private boolean hasBeenClosed = false;
/**
* Internal buffer where data is stored.
*/
private byte[] buf;
/**
* The number of valid bytes in the buffer.
*/
private int count;
/**
* Remembers the size of the buffer.
*/
private int curBufLength;
/**
* The logger to write to.
*/
private Logger log = LoggerFactory.getLogger(LoggingOutputStream.class);
public LoggingOutputStream(){
curBufLength = DEFAULT_BUFFER_LENGTH;
buf = new byte[curBufLength];
count = 0;
}
/**
*
* Writes the specified byte to this output stream.
*
* #param b the byte to write
* #throws IOException if an I/O error occurs.
*/
public void write(final int b) throws IOException {
if (hasBeenClosed) {
log.error("The stream has been closed.");
throw new IOException("The stream has been closed.");
}
// don't log nulls
if (b == 0) {
return;
}
// would this be writing past the buffer?
if (count == curBufLength) {
// grow the buffer
final int newBufLength = curBufLength +
DEFAULT_BUFFER_LENGTH;
final byte[] newBuf = new byte[newBufLength];
System.arraycopy(buf, 0, newBuf, 0, curBufLength);
buf = newBuf;
curBufLength = newBufLength;
}
buf[count] = (byte) b;
count++;
}
/**
* Flushes this output stream and forces any buffered output
* bytes to be written out.
*/
#Override
public void flush() {
if (count == 0) {
return;
}
final byte[] bytes = new byte[count];
System.arraycopy(buf, 0, bytes, 0, count);
String str = new String(bytes);
log.error(str);
count = 0;
}
/**
* Closes this output stream and releases any system resources
* associated with this stream.
*/
#Override
public void close() {
flush();
hasBeenClosed = true;
}
}
Now, all errors are "intercepted" and routed to my log file.
I need to junit test an existing code by the output ( in system.out )
public static void main(String args[])
{
Console console = System.console();
String str = console.readLine();
System.out.println("halo"+str);
}
Since the console.readline is waiting for user input, I found it will halt in the console.readline(),thus block me from getting the output.
Is there a way to do this? Is it posible to do it using Mockito or something?
It is nearly impossible to test code that works with System.console(), because you System.console() always retuns null in tests. Additionally Console is final class without a public constructor.
I recommend to work with the writers and readers of Console and test the code that works with the writers and readers.
public static void main(String args[]) {
Console console = System.console();
BufferedReader reader = new BufferedReader(console.reader());
main2(reader, console.writer(), args);
}
static void main2(BufferedReader reader, Writer writer, String args[] {
String str = reader.readline();
System.out.println("halo"+str);
}
Output to System.out can be tested with the StandardOutputStreamLog rule of the System Rules library. But in your code it is much easier to write to the Console's writer instead of System.out.
The main method is not a good candidate for unit testing. It is just an entry point for an application. Probably, you need to refactor your class to some kind of dependency injection. For example:
public class Something {
private final LinesProvider linesProvider;
public Something(LinesProvider linesProvider) {
this.linesProvider = linesProvider;
}
public void sayHello() {
String str = linesProvider.readLine();
System.out.println("halo "+str);
}
public static void main(String args[]) {
new Something(new LinesProvider() {
private final Console console = System.console();
#Override
public String readLine() {
return console.readLine();
}
}).sayHello();
}
}
interface LinesProvider {
String readLine();
}
Then, you can test the system output like shown below, though it's not a good practice. Instead, try to separate I/O logic from application logic:
public class TestSomething {
private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
private PrintStream printStream = new PrintStream(outputStream, true);
#Before
public void setUp() {
System.setOut(printStream);
}
#Test
public void test() {
new Something(new LinesProvider() {
#Override
public String readLine() {
return "Vasya";
}
}).sayHello();
assertEquals("halo Vasya" + System.getProperty("line.separator"), new String(outputStream.toByteArray()));
}
}
The following test will work, using the JMockit mocking API:
#Test
public void testMyApp(#Mocked final Console console)
{
new NonStrictExpectations(System.class) {{
System.console(); result = console;
console.readLine(); result = " test";
}};
OutputStream output = new ByteArrayOutputStream();
System.setOut(new PrintStream(output));
MyApp.main(new String[0]);
String lineSep = System.getProperty("line.separator");
assertEquals("halo test" + lineSep, output.toString());
}
So my goal is to implement the write method in the class OutputStream to create a new class NumStream, which basically converts ints to Strings. Here is my sample code:
import java.io.*;
public class NumStream extends OutputStream {
public void write(int c) throws IOException {
// What goes here?
}
public static void main(String[] args) {
NumStream ns = new NumStream();
PrintWriter pw = new PrintWriter(new OutputStreamWriter(ns));
pw.println("123456789 and ! and # ");
pw.flush(); // needed for anything to happen, try taking it out
}
}
I've tried using several different approaches, and my result always results in the program compiling, but when I run it, nothing happens. So far I've tried using switch statements to produce this result:
public void write(int c) throws IOException {
StringBuffer sb = new StringBuffer();
switch (c) {
case 1: sb.append("1");
break;
//etc. through 9
I'm unsure of what to do or try next to produce a result. :/ Any tips to steer me in the right direction?
I had the same problem too, Here is the solution:
public class MyOutputStream extends OutputStream {
StringBuilder anotatedText;
public MyOutputStream() {
// Custom constructor
}
#Override
public void write(int b) {
int[] bytes = {b};
write(bytes, 0, bytes.length);
}
public void write(int[] bytes, int offset, int length) {
String s = new String(bytes, offset, length);
anotatedText.append(s);
}
public void myPrint() {
System.out.println(anotatedText);
}
}
All we need to do is to implement the "write" method correctly which is clearly instructed in the above example.
What's the best way to record the size of certain objects as they are being serialized? For example, once objects of type A, B, C are serialized, record the size of their serialized bytes. We can get the size of the entire object graph via getBytes, but we'd like to break it down as to what are the largest contributors to the overall serialized size.
ObjectOutputStream offers writeObjectOverride, but we don't want to rewrite the serialization process. In simplified terms, we need to be aware of when we encounter a certain object prior to serialization, record the total current byte count, and then after it's serialized, take the difference of byte counts. It seems like encompassing writeSerialData would work, but the method is private.
Ideas?
Thanks.
--- UPDATE ---
The answers/suggestions below are insightful. Below is what I have so far. Let me know your thoughts. Thanks.
// extend to get a handle on outputstream
MyObjectOutputStream extends ObjectOutputStream {
private OutputStream out;
public MyObjectOutputStream(out) {
super(out);
this.out = out;
}
public OutputStream getOut() {
return this.out;
}
}
// counter
public static class CounterOutputStream extends FilterOutputStream {
private int bytesWritten = 0;
...
public int getBytesWritten() {
return this.bytesWritten;
}
public void resetCounter() {
bytesWritten = 0;
}
private void update(int len) {
bytesWritten += len;
}
}
// go serialize
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new MyObjectOutputStream(new CounterOutputStream(out, 1024));
// record serialized size of this class; do this for every interested class
public class MyInterestingObject {
...
private void writeObject(ObjectOutputStream out) throws IOException {
CounterOutputStream counter = null;
if (out instanceof MyObjectOutputStream) {
counter = (CounterOutputStream)((MyObjectOutputStream)out).getOut();
counter.resetCounter();
}
// continue w/ standard serialization of this object
out.defaultWriteObject();
if (counter != null) {
logger.info(this.getClass() + " bytes written: " + counter.getBytesWritten());
// TODO: store in context or somewhere to be aggregated post-serialization
}
}
}
The simplest solution would be to wrap the OutputStream you're using with an implementation that will count bytes written.
import java.io.IOException;
import java.io.OutputStream;
public class CountingOutputStream extends OutputStream {
private int count;
private OutputStream out;
public CountingOutputStream(OutputStream out) {
this.out = out;
}
public void write(byte[] b) throws IOException {
out.write(b);
count += b.length;
}
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
count += len;
}
public void flush() throws IOException {
out.flush();
}
public void close() throws IOException {
out.close();
}
public void write(int b) throws IOException {
out.write(b);
count++;
}
public int getBytesWritten() {
return count;
}
}
Then you would just use that
CountingOutputStream s = new CountingOutputStream(out);
ObjectOutputStream o = new ObjectOutputStream(s);
o.write(new Object());
o.close();
// s.getBytesWritten()
You could implement Externalizable rather than Serializable on any objects you need to capture such data from. You could then implement field-by-field byte counting in the writeExternal method, maybe by handing off to a utility class. Something like
public void writeExternal(ObjectOutput out) throws IOException
{
super.writeExternal(out);
out.writeUTF(this.myString == null ? "" : this.myString);
ByteCounter.getInstance().log("MyClass", "myString", this.myString);
}
Another hackish way would be to stick with Serializable, but to use the readResolve or writeReplace hooks to capture whatever data you need, e.g.
public class Test implements Serializable
{
private String s;
public Test(String s)
{
this.s = s;
}
private Object readResolve()
{
System.err.format("%s,%s,%s,%d\n", "readResolve", "Test", "s", s.length());
return this;
}
private Object writeReplace()
{
System.err.format("%s,%s,%s,%d\n", "writeReplace", "Test", "s", s.length());
return this;
}
public static void main(String[] args) throws Exception
{
File tmp = File.createTempFile("foo", "tmp");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(tmp));
Test test = new Test("hello world");
out.writeObject(test);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream(tmp));
test = (Test)in.readObject();
in.close();
}
}