FileHandler - java.logging.util no carriage return - java

I'm trying to print log information on file using java.logging.util. This solution is working, but the information from the log doesn't show with the carriage return.
My code:
Main
String url ="opc.tcp://DEV85:53530/OPCUA/SimulationServer";
MyFormatter formatter=new MyFormatter();
fh = new FileHandler("C:/tmp/MyLogFile.log",true);
logger.addHandler(fh);
fh.setFormatter(formatter);
doSomething else()
EndpointDescription[] endpoints = myClient.discoverEndpoints(url);
for(EndpointDescription e: endpoints) {
//System.out.println(e);
logger.info(e.toString());
}
Formatter Class
public class MyFormatter extends Formatter {
// Create a DateFormat to format the logger timestamp.
private static final DateFormat df = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss.SSS");
#Override
public String format(LogRecord record) {
StringBuilder builder = new StringBuilder(1000);
builder.append(df.format(new Date(record.getMillis()))).append(" - ");
builder.append("[").append(record.getSourceClassName()).append(".");
builder.append(record.getSourceMethodName()).append("] - ");
builder.append("[").append(record.getLevel()).append("] - ");
builder.append(formatMessage(record));
builder.append(System.getProperty("line.separator", "\r\n"));
//builder.append("\n");
return builder.toString();
}
}
The output is just on a line. I've found out online some solution, but nothing it's working for me.

One quick nit, the SimpleDateFormat is not thread-safe so you don't what to store it as a object or static reference. Create a new instance each call or use the java.time.format package. Fix this so it is not causing any phantom issues.
This solution is working, but the information from the log doesn't show with the carriage return.
Open the file with a text editor that can show whitespace characters. Your formatter code looks correct. You need to double check that the viewer you are using may be just rendering the text incorrectly (Notepad). Worst case use a FileInputStream and read raw bytes as hex. Should be able to see the hex values for the line separator. Your unit test of just the formatter itself should be able to verify that the line separator is present.
The other issue is that you have to prove that your formatter is being used. You are opening the file for append so it would be possible for other formatters to insert their data in the same file. Consider overriding getHead and getTail to generate a unique id and place that in your formatter for testing. This will show the start and stop of your formatter.
Also the java.util.logging.SimpleFormatter can support this format but it is a global setting and can't be customized for each object.

Related

Print log message in the same line [duplicate]

At the moment a default entry looks something like this:
Oct 12, 2008 9:45:18 AM myClassInfoHere
INFO: MyLogMessageHere
How do I get it to do this?
Oct 12, 2008 9:45:18 AM myClassInfoHere - INFO: MyLogMessageHere
Clarification I'm using java.util.logging
As of Java 7, java.util.logging.SimpleFormatter supports getting its format from a system property, so adding something like this to the JVM command line will cause it to print on one line:
-Djava.util.logging.SimpleFormatter.format='%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$s %2$s %5$s%6$s%n'
Alternatively, you can also add this to your logger.properties:
java.util.logging.SimpleFormatter.format='%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$s %2$s %5$s%6$s%n'
Similar to Tervor, But I like to change the property on runtime.
Note that this need to be set before the first SimpleFormatter is created - as was written in the comments.
System.setProperty("java.util.logging.SimpleFormatter.format",
"%1$tF %1$tT %4$s %2$s %5$s%6$s%n");
1) -Djava.util.logging.SimpleFormatter.format
Java 7 supports a property with the java.util.Formatter format string syntax.
-Djava.util.logging.SimpleFormatter.format=...
See here.
My favorite is:
-Djava.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$-6s %2$s %5$s%6$s%n
which makes output like:
2014-09-02 16:44:57 SEVERE org.jboss.windup.util.ZipUtil unzip: Failed to load: foo.zip
2) Putting it to IDEs
IDEs typically let you set system properties for a project.
E.g. in NetBeans, instead of adding -D...=... somewhere, add the property in the action dialog, in a form of java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-... - without any quotes. The IDE should figure out.
3) Putting that to Maven - Surefire
For your convenience, Here is how to put it to Surefire:
<!-- Surefire -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.17</version>
<configuration>
<systemPropertyVariables>
<!-- Set JUL Formatting -->
<java.util.logging.SimpleFormatter.format>%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$-6s %2$s %5$s%6$s%n</java.util.logging.SimpleFormatter.format>
</systemPropertyVariables>
</configuration>
</plugin>
4) Hand-made
I have a library with few java.util.logging related classes. Amongst them, it's SingleLineFormatter.
Downloadable jar here.
public class SingleLineFormatter extends Formatter {
Date dat = new Date();
private final static String format = "{0,date} {0,time}";
private MessageFormat formatter;
private Object args[] = new Object[1];
// Line separator string. This is the value of the line.separator
// property at the moment that the SimpleFormatter was created.
//private String lineSeparator = (String) java.security.AccessController.doPrivileged(
// new sun.security.action.GetPropertyAction("line.separator"));
private String lineSeparator = "\n";
/**
* Format the given LogRecord.
* #param record the log record to be formatted.
* #return a formatted log record
*/
public synchronized String format(LogRecord record) {
StringBuilder sb = new StringBuilder();
// Minimize memory allocations here.
dat.setTime(record.getMillis());
args[0] = dat;
// Date and time
StringBuffer text = new StringBuffer();
if (formatter == null) {
formatter = new MessageFormat(format);
}
formatter.format(args, text, null);
sb.append(text);
sb.append(" ");
// Class name
if (record.getSourceClassName() != null) {
sb.append(record.getSourceClassName());
} else {
sb.append(record.getLoggerName());
}
// Method name
if (record.getSourceMethodName() != null) {
sb.append(" ");
sb.append(record.getSourceMethodName());
}
sb.append(" - "); // lineSeparator
String message = formatMessage(record);
// Level
sb.append(record.getLevel().getLocalizedName());
sb.append(": ");
// Indent - the more serious, the more indented.
//sb.append( String.format("% ""s") );
int iOffset = (1000 - record.getLevel().intValue()) / 100;
for( int i = 0; i < iOffset; i++ ){
sb.append(" ");
}
sb.append(message);
sb.append(lineSeparator);
if (record.getThrown() != null) {
try {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
record.getThrown().printStackTrace(pw);
pw.close();
sb.append(sw.toString());
} catch (Exception ex) {
}
}
return sb.toString();
}
}
Like Obediah Stane said, it's necessary to create your own format method. But I would change a few things:
Create a subclass directly derived from Formatter, not from SimpleFormatter. The SimpleFormatter has nothing to add anymore.
Be careful with creating a new Date object! You should make sure to represent the date of the LogRecord. When creating a new Date with the default constructor, it will represent the date and time the Formatter processes the LogRecord, not the date that the LogRecord was created.
The following class can be used as formatter in a Handler, which in turn can be added to the Logger. Note that it ignores all class and method information available in the LogRecord.
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
public final class LogFormatter extends Formatter {
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
#Override
public String format(LogRecord record) {
StringBuilder sb = new StringBuilder();
sb.append(new Date(record.getMillis()))
.append(" ")
.append(record.getLevel().getLocalizedName())
.append(": ")
.append(formatMessage(record))
.append(LINE_SEPARATOR);
if (record.getThrown() != null) {
try {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
record.getThrown().printStackTrace(pw);
pw.close();
sb.append(sw.toString());
} catch (Exception ex) {
// ignore
}
}
return sb.toString();
}
}
This is what I'm using.
public class VerySimpleFormatter extends Formatter {
private static final String PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
#Override
public String format(final LogRecord record) {
return String.format(
"%1$s %2$-7s %3$s\n",
new SimpleDateFormat(PATTERN).format(
new Date(record.getMillis())),
record.getLevel().getName(), formatMessage(record));
}
}
You'll get something like...
2016-08-19T17:43:14.295+09:00 INFO Hey~
2016-08-19T17:43:16.068+09:00 SEVERE Seriously?
2016-08-19T17:43:16.068+09:00 WARNING I'm warning you!!!
Per screenshot, in Eclipse select "run as" then "Run Configurations..." and add the answer from Trevor Robinson with double quotes instead of quotes. If you miss the double quotes you'll get "could not find or load main class" errors.
I've figured out a way that works. You can subclass SimpleFormatter and override the format method
public String format(LogRecord record) {
return new java.util.Date() + " " + record.getLevel() + " " + record.getMessage() + "\r\n";
}
A bit surprised at this API I would have thought that more functionality/flexibility would have been provided out of the box
If you log in a web application using tomcat add:
-Djava.util.logging.ConsoleHandler.formatter = org.apache.juli.OneLineFormatter
On VM arguments
This logging is specific to your application and not a general Java feature. What application(s) are you running?
It might be that this is coming from a specific logging library that you are using within your own code. If so, please post the details of which one you are using.
if you're using java.util.logging, then there is a configuration file that is doing this to log contents (unless you're using programmatic configuration). So, your options are
1) run post -processor that removes the line breaks
2) change the log configuration AND remove the line breaks from it. Restart your application (server) and you should be good.

CSVWriter adds blank line beetween each row

I am trying to write some data into a csv file but some of them are written at the end of the line and there is a blank space between each row. I have also tried do collect data into an ArrayList of strings and then use the method csvWriter.writeAll but it gives me the same result.
try(FileWriter fileWriter1 = new FileWriter(csvPath)){
CSVWriter csvWriter = new CSVWriter(fileWriter1);
//Impostazione di Git e della repo.
Git git = Git.open(new File(Cpath));
Repository repository = FileRepositoryBuilder.create(new File(Cpath));
String repo = String.valueOf(repository);
logger.info(repo);
List<Ref> branches = git.branchList().call();
for (Ref ref : branches)
{
logger.info(ref.getName());
}
Iterable<RevCommit> commits = git.log().all().call();
for (RevCommit revCommit : commits) { //itero tutti i commit.
String pattern = "MM/dd/yyyy HH:mm:ss";
DateFormat df = new SimpleDateFormat(pattern);
String date = df.format(revCommit.getAuthorIdent().getWhen());
String[] columns = {date, revCommit.getFullMessage()};
csvWriter.writeNext(new String[]{date, revCommit.getFullMessage()});
}
csvWriter.flush();
csvWriter.close();
}
The CSV format loosely follows the RFC 4180. But most cvs writers consistenly use a \r\n (MS/DOS convention) end of file.
When you read that with an editor (or a terminal) that expects only one single \n for an end of line (Unix convention), you wrongly see additional lines when the file is perfectly correct.
Ref: Wikipedia

Java scan a log file and get the time information then calculate the elapsed time

I have a txt file with a line like below:
0 Apr 12 08:42:44.000009 (+0.000009) *** START ***
The information I want to get is:
Apr 12 08:42:44
The current method I'm using use is using a scanner to read this line:
public void getTime() throws IOException {
String time = "";
Scanner scan = new Scanner(location);
String firstLine = scan.nextLine();
String[] splitString = firstLine.split("\\.");
String[] rebootTime = splitString[0].split(" ");
for(int i = 0; i < rebootTime.length; i++) {
if(i != 0) {
time = time + rebootTime[i] + " ";
}
}
System.out.println(time);
}
Is there a smarter way to get the time information?
After I get the time, how do I transfer it to a date format then calculate the duration?
I'm trying to use JAVA 8 Instant with this method, how can I transfer the time value to a Instant type?
If I understand you properly your goal is to extract reboot time from each string of a log file. Using Scanner is ok to my mind. After extracting a line from log file you might as well use regular expressions on it, like this:
String firstLine = scan.nextLine();
Pattern pattern = Pattern.compile("[a-zA-Z]{3}\\s\\d{2}\\s\\d{2}:\\d{2}:\\d{2}");
Matcher matcher = pattern.matcher(firstLine);
if (matcher.find()) {
String rebootTime = matcher.group();
}
This regexp is not perfect but it works on your line. You can make it more or less strict.
As to formatting the string to a LocalDateTime, you can use following method:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("MMM dd HH:mm:ss")
.parseDefaulting(ChronoField.YEAR, 1)
.toFormatter();
LocalDateTime date = LocalDateTime.parse(rebootTime, formatter);
So parseDefaulting(ChronoField.YEAR, 1) means that you ignore year in string and set it as 1 in resulting LocalDateTime. After that you can calculate durations using LocalDateTimes.
I like Mongwo's elegant solution.
There are many ways to skin this cat. Other than regular expression, you can simply use a quick-n-dirty one liner, if it is in fixed length and always starting from the fixed index of a string:
String rawStr = "0 Apr 12 08:42:44.000009 (+0.000009) *** START ***";
System.out.println(rawStr.substring(5, 20));
If the file is small enough that you are ok with reading the whole thing, it is generated by another process so that you can guarantee the format, and the line you want is first (which from the question I think all above things should be true), your solution can be as simple as
private SimpleDateFormat format = new SimpleDateFormat("MMM dd HH:mm:ss");
public Date read(String filePath) throws URISyntaxException, IOException, ParseException {
Path fileLocation = Paths.get(filePath);
byte[] data = Files.readAllBytes(fileLocation);
return format.parse(new String(data, 5, 15));
}
If the file is longer, you may want to use a scanner if you dont want to read the whole thing, but imho it is still simplest to use indices to get the part of the string that you want.
If you want a very elegant solution maybe you could use a regex, but I really dont think there is much of a need.
My try here is:-
First let's extract value before dot(".") and then value after double space(" ").
public static void main(String[] args) {
String str = "0 Apr 12 08:42:44.000009 (+0.000009) *** START ***";
String[] str1 = str.split("\\.");
String[] str2 = str1[0].split("\\s{2,}");
System.out.println((str2[1]));
}
To understand \\s{2,} you can look into saved regex.
regex to match 2 spaces

Univocity - writing out quote escape character using RowWriterProcessor

I am using univocity to parse files to beans, perform some magic on the objects, and afterwards write the beans back to a file. The code snippet that duplicates my problem:
public class Sample {
public static void main(String[] args) throws IOException {
BeanListProcessor<Person> rowProcessor = new BeanListProcessor<Person>(Person.class);
CsvParserSettings parserSettings = new CsvParserSettings();
parserSettings.setProcessor(rowProcessor);
parserSettings.getFormat().setDelimiter(',');
parserSettings.getFormat().setQuote('"');
parserSettings.getFormat().setQuoteEscape('/');
CsvParser parser = new CsvParser(parserSettings);
parser.parse(new FileReader("src/main/resources/person.csv"));
List<Person> beans = rowProcessor.getBeans();
Writer outputWriter = new FileWriter("src/main/resources/personOut.csv", true);
CsvWriterSettings settings = new CsvWriterSettings();
settings.getFormat().setDelimiter(',');
settings.getFormat().setQuote('"');
settings.getFormat().setQuoteEscape('/');
settings.setRowWriterProcessor(new BeanWriterProcessor<Person>(Person.class));
CsvWriter writer = new CsvWriter(outputWriter, settings);
for (Person person : beans) {
writer.processRecord(person);
}
writer.close();
}
}
Bean class (getters and setters ommited):
public class Person {
#Parsed(index = 0)
private int id;
#Parsed(index = 1)
private String firstName;
#Parsed(index = 2)
private String lastName;
}
And the file content (input):
1,"Alex ,/,awesome/,",chan
2,"Peter ,boring",pitt
The file is not under my control as it is being provided by an external party. After the application performs operation on the objects (not included in the code snippet), I need to return the file to the external party with exactly the same settings.
Desired file output content:
1,"Alex ,/,awesome/,",chan
2,"Peter ,boring",pitt
However I am getting:
1,"Alex ,,awesome,",chan
2,"Peter ,boring",pitt
Is there a way to include the actual used quote escape character when writing the beans back to a file?
EDIT
I have tried using settings.setQuoteAllFields(true); on the writer and all combinations of
parserSettings.setKeepQuotes(true);
parserSettings.setKeepEscapeSequences(true);
on the CsvParserSettings, but none seem to give me the result I am looking for.
The solution for this is divided in two parts:
First, please make sure you use version 2.2.3, as it's been just released with an adjustment to properly capture the quote escape character if it's not been escaped, which is your case:
Here the / is not escaped:
"Alex ,/,awesome/,"
Versions prior to 2.2.3 would expect this:
"Alex ,//,awesome//,"
Second, when writing this back to the output, you won't want to write the escape escape, i.e., given the string "Alex ,/,awesome/,"
you want to get:
"Alex ,/,awesome/,"
Instead of what it will do by default:
"Alex ,//,awesome//,"
On the CsvWriterSettings, do this:
settings.getFormat().setCharToEscapeQuoteEscaping('\0');
And it will not try to escape the quote escape character.
Hope it helps

Logging with Logger under Android

I've noticed that the following works on PC but not inside the Android simulator.
Logger logger = Logger.getLogger("foo");
logger.log(Level.INFO, "Number is: {0}", new Object[]{new Integer(42)});
It just prints
Number is: {0}
Why does this fail for android?
I found a solution in a roundabout way. Ultimately, I think there is a Formatter somewhere in the default android logging implementation which is rendering just the message, ignoring the params. You should be able to use the Logging API to install a formatter of your own design.
I had already installed a new Handler for a totally separate reason. The source I used is available here: http://4thline.org/projects/download/misc/
The handler is in the teleal-common-1.0.14-source.tar.gz archive. Follow the path to src\main\java\org\fourthline\android\util\FixedAndroidHandler.java.
This site also provides code to install this handler, but it's in a different archive: sash-1.0-SNAPSHOT.tar.gz. Locate 1.0\src\main\java\org\teleal\common\logging\LoggingUtil.java.
You can install this handler by making this call somewhere in your app startup code:
LoggingUtil.resetRootHandler(new FixedAndroidHandler());
What I found was that the Formatter for this Handler is embedded as an anonymous class inside the Handler. How convenient. I could see that the Formatter did not process the parameters passed in via the LogRecord. I just added an "else-if" condition:
private static final Formatter THE_FORMATTER = new Formatter() {
#Override
public String format(LogRecord r) {
String msg = r.getMessage();
Object[] params = r.getParameters();
Throwable thrown = r.getThrown();
if (thrown != null) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
sw.write(r.getMessage());
sw.write("\n");
thrown.printStackTrace(pw);
pw.flush();
return sw.toString();
} else if ((params != null) && (params.length > 0) && (msg.indexOf("{0") >= 0)) {
return MessageFormat.format(msg, params);
} else {
return r.getMessage();
}
}
};
The if test is consistent with other log formatting code I've seen. In theory you should be able to take a more direct approach of installing a similar Formatter to an existing Handler. I haven't tried this myself due to the fact that the above solution is working for me.
You can accomplish the same String formatting using the static String.format(String format, Object... args) method:
Log.d(TAG, String.format("Number is: %d", new Integer(42)));
Use:
android.util.Log.i(TAG, "Number is: " + number);
See http://developer.android.com/reference/android/util/Log.html

Categories