I'm pretty new in Java but I have to write a simple logger.
My problem is that my logger won't transfer its handler.
My first class is:
public class LoggerClass {
static final Logger logger = Logger.getLogger("myLogger");
FileHandler fh;
public LoggerClass() {
try {
this.fh = new FileHandler("C:/Users/Administrator/Desktop/Logging%u.txt");
} catch (IOException | SecurityException ex) {
Logger.getLogger(LoggerClass.class.getName()).log(Level.SEVERE, null, ex);
}
logger.addHandler(fh);
SimpleFormatter formatter = new SimpleFormatter();
fh.setFormatter(formatter);
}
}
and i want to transfer the logger to another class by doing this:
package Logger;
import static Logger.LoggerClass.logger;
import java.io.IOException;
import java.util.logging.Level;
public class LoggingTest {
public static void main(String[] args) throws IOException {
try
{
((Object) null).toString();
}
catch ( Exception e )
{
logger.log( Level.SEVERE, "oh oh", e );
}
logger.info( "Hat funktioniert" );
}
}
i tried nearly everything that i've found, and the logger functions but it only gives its output on console and not how it should be in a file
In your test you never configured your logger. Try this:
package Logger;
import static Logger.LoggerClass.logger;
import java.io.IOException;
import java.util.logging.Level;
//There are better ways to do this.
static {
new LoggerClass();
}
public class LoggingTest {
public static void main(String[] args) throws IOException {
try
{
((Object) null).toString();
}
catch ( Exception e )
{
logger.log( Level.SEVERE, "oh oh", e );
}
logger.info( "Hat funktioniert" );
}
}
change this:
static final Logger logger = Logger.getLogger("myLogger");
To
public static final Logger logger = Logger.getLogger("myLogger");
Reason you have given it a default access in your class where you defined Logger and using defail access you can access it in package only. While where you are using is you have another package and hence error.
Apart form above use logger like:
LoggerInterface.logger.info( "Hat funktioniert" );
Since you defined it static so use classname.instance.method(parameter)
Related
I have a simple class that writes data to a CSV file:
public class CsvFileWriter {
private static Logger logger = LogManager.getLogger();
public static void writeDataToCsvFile(String filePath, List<String[]> data) {
File file = new File(filePath);
try (FileWriter outputFile = new FileWriter(file)) {
CSVWriter writer = new CSVWriter(outputFile, ' ',
CSVWriter.NO_QUOTE_CHARACTER,
CSVWriter.DEFAULT_ESCAPE_CHARACTER,
CSVWriter.DEFAULT_LINE_END);
writer.writeAll(data);
} catch (IOException e) {
logger.error("Unable to find the specified path", e);
throw new ProjectException(e);
}
}
}
I would like to know how to test the method contained in this class. I found information that this can be done using Mockito. Is it so? and how can i do this?
You don't need Mockito or any mocking in order to test that (with exception of failure handling and logging).
You should use #TempDir from Junit 5, or TemporaryFolder from Junit 4
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
class CsvFileWriterTest {
#Test
void createsCsvFile(#TempDir final Path temp) {
final var csv = temp.resolve("test.csv");
CsvFileWriter.writeDataToCsvFile(
csv.toString(),
List.of(
new String[]{"1", "abc"},
new String[]{"2", "def"}
)
);
Assertions.assertTrue(csv.toFile().exists());
}
#Test
void writesDataToCsv(#TempDir final Path temp) throws IOException {
final var csv = temp.resolve("test.csv");
CsvFileWriter.writeDataToCsvFile(
csv.toString(),
List.of(
new String[]{"1", "abc"},
new String[]{"2", "def"}
)
);
Assertions.assertEquals(
Files.readAllLines(csv),
List.of("1 abc", "2 def")
);
}
}
Though you might want to refactor your class in order to be more testable:
#lombok.RequiredArgsConstructor
public class CsvFileWriter {
private static final Logger LOG = LogManager.getLogger();
private final Path file;
private final Function<Writer, CSVWriter> csvfactory;
public CsvFileWriter(final Path file) {
this(
file,
writer ->
new CSVWriter(writer, ' ',
CSVWriter.NO_QUOTE_CHARACTER,
CSVWriter.DEFAULT_ESCAPE_CHARACTER,
CSVWriter.DEFAULT_LINE_END
)
);
}
public void write(final List<String[]> data) {
try (var csv = this.csvfactory.apply(Files.newBufferedWriter(this.file))) {
csv.writeAll(data);
} catch (IOException ex) {
LOG.error("Unable to find the specified path", ex);
throw new UncheckedIOException(ex);
}
}
}
Now you can pass a factory method to constructor, and use it in order to mock CSVWriter. For example you can test that IOException is handled correcly by you class:
#Test
void handlesIoException(#TempDir final Path temp) {
final var mock = Mockito.mock(CSVWriter.class);
Mockito.doThrow(IOException.class).when(mock).writeAll(Mockito.anyList());
final var writer = new CsvFileWriter(temp.resolve("not.csv"), wrt -> mock);
Assertions.assertThrows(
UncheckedIOException.class,
() -> writer.write(List.of())
);
}
I want to add exceptions in a single log file, In my source code following code is used multiple times... i want to store all exceptions in single file, but it creates multiple files like exception.log, exception.log.1, exception.log.1.lck, exception.log.2 and so on...
Date dir1 = new java.util.Date(System.currentTimeMillis());
String baseDir1 = "/home/gaurav/usr/logs/ESBegin/";
String newDir1 = createDateBasedDirectory(baseDir1, dir1);
System.out.println("Exception :: " + e.getMessage());
Logger logger = Logger.getLogger("MyLog");
FileHandler fh;
try {
// This block configure the logger with handler and formatter
fh = new FileHandler(newDir1+"/exception.log");
logger.addHandler(fh);
SimpleFormatter formatter = new SimpleFormatter();
fh.setFormatter(formatter);
// the following statement is used to log any messages
logger.info(e.getMessage());
} catch (SecurityException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
It's because you create a new FileHandler each time you call that method, instead make it a class instance variable. I tested it and it works:
import java.io.IOException;
import java.util.Date;
import java.util.logging.FileHandler;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
public class Main {
private static String newDir1 = "C:\\Users\\pavel.orekhov\\Desktop";
private static FileHandler fh;
static {
try {
fh = new FileHandler(newDir1 + "\\exception.log", 0, 1, true);
} catch (IOException | SecurityException e) {
}
}
static void test() {
Date dir1 = new java.util.Date(System.currentTimeMillis());
Logger logger = Logger.getLogger("MyLog");
logger.addHandler(fh);
SimpleFormatter formatter = new SimpleFormatter();
fh.setFormatter(formatter);
logger.info("test");
}
public static void main(String[] args) {
// all these write to the same file
test();
test();
test();
test();
}
}
The code that you had was something like this:
import java.io.IOException;
import java.util.Date;
import java.util.logging.FileHandler;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
public class Main {
static void test() {
Date dir1 = new java.util.Date(System.currentTimeMillis());
String newDir1 = "C:\\Users\\pavel.orekhov\\Desktop";
Logger logger = Logger.getLogger("MyLog");
FileHandler fh;
try {
fh = new FileHandler(newDir1 + "\\exception.log", 0, 1, true);
logger.addHandler(fh);
SimpleFormatter formatter = new SimpleFormatter();
fh.setFormatter(formatter);
logger.info("test");
} catch (SecurityException | IOException ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
test(); // creates file 1
test(); // creates file 2
test(); // creates file 3
test(); // creates file 4
}
}
this does create as many files as you have calls to the test() method.
EDIT: This does not seem to be possible, see https://bugs.openjdk.java.net/browse/JDK-8039910.
I have a helper class that provides a Stream<Path>. This code just wraps Files.walk and sorts the output:
public Stream<Path> getPaths(Path path) {
return Files.walk(path, FOLLOW_LINKS).sorted();
}
As symlinks are followed, in case of loops in the filesystem (e.g. a symlink x -> .) the code used in Files.walk throws an UncheckedIOException wrapping an instance of FileSystemLoopException.
In my code I would like to catch such exceptions and, for example, just log a helpful message. The resulting stream could/should just stop providing entries as soon as this happens.
I tried adding .map(this::catchException) and .peek(this::catchException) to my code, but the exception is not caught in this stage.
Path checkException(Path path) {
try {
logger.info("path.toString() {}", path.toString());
return path;
} catch (UncheckedIOException exception) {
logger.error("YEAH");
return null;
}
}
How, if at all, can I catch an UncheckedIOException in my code giving out a Stream<Path>, so that consumers of the path do not encounter this exception?
As an example, the following code should never encounter the exception:
List<Path> paths = getPaths().collect(toList());
Right now, the exception is triggered by code invoking collect (and I could catch the exception there):
java.io.UncheckedIOException: java.nio.file.FileSystemLoopException: /tmp/junit5844257414812733938/selfloop
at java.nio.file.FileTreeIterator.fetchNextIfNeeded(FileTreeIterator.java:88)
at java.nio.file.FileTreeIterator.hasNext(FileTreeIterator.java:104)
at java.util.Iterator.forEachRemaining(Iterator.java:115)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at ...
EDIT: I provided a simple JUnit test class. In this question I ask you to fix the test by just modifying the code in provideStream.
package somewhere;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.nio.file.FileVisitOption.FOLLOW_LINKS;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.fail;
public class StreamTest {
#Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
#Test
public void test() throws Exception {
Path rootPath = Paths.get(temporaryFolder.getRoot().getPath());
createSelfloop();
Stream<Path> stream = provideStream(rootPath);
assertThat(stream.collect(Collectors.toList()), is(not(nullValue())));
}
private Stream<Path> provideStream(Path rootPath) throws IOException {
return Files.walk(rootPath, FOLLOW_LINKS).sorted();
}
private void createSelfloop() throws IOException {
String root = temporaryFolder.getRoot().getPath();
try {
Path symlink = Paths.get(root, "selfloop");
Path target = Paths.get(root);
Files.createSymbolicLink(symlink, target);
} catch (UnsupportedOperationException x) {
// Some file systems do not support symbolic links
fail();
}
}
}
You can make your own walking stream factory:
public class FileTree {
public static Stream<Path> walk(Path p) {
Stream<Path> s=Stream.of(p);
if(Files.isDirectory(p)) try {
DirectoryStream<Path> ds = Files.newDirectoryStream(p);
s=Stream.concat(s, StreamSupport.stream(ds.spliterator(), false)
.flatMap(FileTree::walk)
.onClose(()->{ try { ds.close(); } catch(IOException ex) {} }));
} catch(IOException ex) {}
return s;
}
// in case you don’t want to ignore exceprions silently
public static Stream<Path> walk(Path p, BiConsumer<Path,IOException> handler) {
Stream<Path> s=Stream.of(p);
if(Files.isDirectory(p)) try {
DirectoryStream<Path> ds = Files.newDirectoryStream(p);
s=Stream.concat(s, StreamSupport.stream(ds.spliterator(), false)
.flatMap(sub -> walk(sub, handler))
.onClose(()->{ try { ds.close(); }
catch(IOException ex) { handler.accept(p, ex); } }));
} catch(IOException ex) { handler.accept(p, ex); }
return s;
}
// and with depth limit
public static Stream<Path> walk(
Path p, int maxDepth, BiConsumer<Path,IOException> handler) {
Stream<Path> s=Stream.of(p);
if(maxDepth>0 && Files.isDirectory(p)) try {
DirectoryStream<Path> ds = Files.newDirectoryStream(p);
s=Stream.concat(s, StreamSupport.stream(ds.spliterator(), false)
.flatMap(sub -> walk(sub, maxDepth-1, handler))
.onClose(()->{ try { ds.close(); }
catch(IOException ex) { handler.accept(p, ex); } }));
} catch(IOException ex) { handler.accept(p, ex); }
return s;
}
}
I have MyLogger class which contains:
public class MyLogger {
private final static Logger logger = Logger.getLogger(MyLogger.class.getName());
private static FileHandler fileHandler;
private static String loggerFileName;
public MyLogger() {
}
public static void createMyLogger(String filename){
loggerFileName = filename;
try {
File loggerFile = new File(filename);
boolean fileExists = loggerFile.exists();
if(fileExists){
loggerFile.delete();
File lockFile = new File(filename+".lck");
if(lockFile.exists())
lockFile.delete();
}
fileHandler = new FileHandler(filename,true);
} catch (IOException e) {
e.printStackTrace();
}
logger.addHandler(fileHandler);
SimpleFormatter simpleFormatter = new SimpleFormatter();
fileHandler.setFormatter(simpleFormatter);
}
public static void log(String msg) {
logger.log(Level.INFO, msg);
}
public static void log(Exception ex) {
logger.log(Level.SEVERE, "Exception",ex);
}
public static String getLoggerFileName() {
return loggerFileName;
}
public static void setLoggerFileName(String loggerFileName) {
MyLogger.loggerFileName = loggerFileName;
}
And I am having my further execution in threads, i.e. When I am starting first process then Logger File is created and logs are recorded, but when I starts another process then again different thread is created and also new logger file is created but because of static methods and reference It mixed up the both process logs in both logger files...
When I start process for every thread following method is called:
public void start(String process) {
try{
String filename = process.replace(".com", "");
MyLogger.createXPathLogger(filename.concat(WordUtils.capitalize(type))+ ".log");
MyLogger.log("got parameters ===>> process : "+process);
}catch (Exception e) {
MyLogger.log("Exception In main() method....");
MyLogger.log("*****"+process+" process failed In main() method.*****");
MyLogger.log(e);
}
}
So what can I do for this, Ho can I do thread safe logging?
thanks in advance..
The idea here won't really work. One logger gets created and you add the each handler to the logger.
What you really need is a concept of a log context. You need to isolate the creation of the logger on a per-thread basis which AFAIK you can't do with J.U.L.
This is a very rough around the edges example of using JBoss Log Manager to do this:
import java.io.File;
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import org.jboss.logmanager.LogContext;
public class MyLogger {
private static final ThreadLocal<MyLogger> LOCAL_LOGGER = new ThreadLocal<MyLogger>();
private final Logger logger;
private MyLogger(final Logger logger) {
this.logger = logger;
}
public static MyLogger createMyLogger(final String filename) {
MyLogger result = LOCAL_LOGGER.get();
if (result == null) {
final LogContext logContext = LogContext.create();
final Logger logger = logContext.getLogger(MyLogger.class.getName());
final FileHandler fileHandler;
try {
final File loggerFile = new File(filename);
if (loggerFile.exists()) {
loggerFile.delete();
File lockFile = new File(filename + ".lck");
if (lockFile.exists())
lockFile.delete();
}
fileHandler = new FileHandler(filename, true);
} catch (IOException e) {
throw new RuntimeException("Could not create file handler", e);
}
logger.addHandler(fileHandler);
fileHandler.setFormatter(new SimpleFormatter());
final MyLogger myLogger = new MyLogger(logger);
LOCAL_LOGGER.set(myLogger);
result = myLogger;
}
return result;
}
public void log(String msg) {
logger.log(Level.INFO, msg);
}
public void log(Exception ex) {
logger.log(Level.SEVERE, "Exception", ex);
}
}
i am trying to read values from properties file and
when i tried to run this program
its giving the output as
null
import java.io.FileInputStream;
import java.util.Properties;
public class JavaApplication1 {
final private static String osName = System.getProperty("os.name");
static final Properties configFile = new Properties() {
{
try {
configFile.load(new FileInputStream("config.properties"));
} catch (Exception e) {
}
}
};
private static String DIR = osName.equals("Linux") ? configFile.getProperty("tempDirForLinux") : configFile.getProperty("tempDirForWindows");
public static void main(String[] args) {
System.out.println(DIR);
}
}
The part that is a bit odd in your example is where you create an anonymous Properties class and then load the properties into that same class in an initialization statement. I'm not sure how that is meant to work (and I'm guessing doesn't)
This is probably what you want rather
public class JavaApplication1 {
final private static String osName = System.getProperty("os.name");
static final Properties configFile = new Properties();
static {
try {
configFile.load(new FileInputStream("config.properties"));
} catch (Exception e) {
e.printStackTrace();
}
};
private static String DIR = osName.equals("Linux") ? configFile.getProperty("tempDirForLinux") : configFile.getProperty("tempDirForWindows");
public static void main(String[] args) throws IOException {
System.out.println(DIR);
}
}