I need to have one single file for the log, but I want to specify two different layouts for this file. I read somewhere that declaring two appenders which write the same file is not advised, so how could do? Thanks in advance.
This seems like a rather odd thing to do, as usually you would want all the lines in a single log file to be the same format, both for ease of eyeballing and if you want to do any automated processing of the log later on. But if you must then you are correct in saying that you shouldn't have two different appenders writing to the same file at the same time.
The solution would probably be to implement a custom Layout which can inspect the log event and then delegate to one of two (or more) other layouts to do the actual formatting
public class MultiLayout extends Layout {
private Layout layout1;
private Layout layout2;
public MultiLayout() {
layout1 = ....;
layout1.activateOptions();
layout2 = ....;
layout2.activateOptions();
}
public boolean ignoresThrowable() {
return layout1.ignoresThrowable();
}
public String format(LoggingEvent e) {
// choose the appropriate layout, e.g. based on logger name
if(e.getLoggerName().startsWith("com.example.")) {
return layout1.format(e);
} else {
return layout2.format(e);
}
}
}
Related
Suppose I have a program with 20 classes, each with private static Logger logger = LoggerFactory.getLogger(SomeClass.class);. These 20 classes will log warn, info, debug, or error.
Within one of the classes, how can I can access to all the errors logged in the program thus far (across all of the classes)? Note I'm not interested in dumping all these to a .log, I want access to them from within the Java program.
Use a custom appender to add to list which is singleton list using that you can access all the mismatch.
CustomAppender.java
public class CustomAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
#Override
protected void append(ILoggingEvent eventObject) {
if(eventObject.getLevel() == Level.WARN){
{
KPLogHolder.addData(eventObject.getMessage);
}
}
}
A singleton java class to hold messages KPLogHolder.java
public class KPLogHolder {
private static List<String> holder;
public static List<String> getLog(){
if(holder== null){
return new ArrayList<String>();
}
return holder;
}
public static void addData(String item){
getLog().add(item);
}
}
And accessing the mssage.
public class KPTest{
#Test
public void testLog(){
LOG.warn("Warning!!!!");
for(String str: KPLogHolder.getLog()){
System.out.println(str);
}
}
}
Note I've not used synchronization you need to implement those as ArrayList is not synced. Not sure why this requirement but be careful. most cases these goes to wrong implementation, there situation were it will bring down your server. if its not implemented properly.
Do you want to access it when all classes have logged the statement in the .log file or you want to keep accessing as and when the logging is happening.
If you want to access all the logs from those 20 classes after the logging funcation then why not read the .log file later in your other java program to access all the logs.
But i guess you need an indicator in your log file to determine those statementa have been logged by those 20 classes. say for example some string in the consoleappender configuration
Not very difficult -
Either
Read log file
or
Overrider your log4j framework so instead of logging to file, it should log data to java objects (this is going to be very memory intensive).
I am reading a header from a text file and adding it to file using log4j.
The header is getting inserted successfully but it inserts header on every restart of my application, but I want the header to be inserted only when the file is created. I am using DailyRollingFileAppender, this would roll every midnight creating a new file.
I am overriding the getHeader method from PatternLayout class to add header to a file.
Can anyone help me out in inserting header only once (i.e. only when file is created)?
log4j.appender.ErrFileAppender=com.logger.ErrorFile
log4j.appender.ErrFileAppender.Threshold=WARN
log4j.appender.ErrFileAppender.File=${logdir}/Error.log
log4j.appender.ErrFileAppender.layout=com.logger.header.ErrHeader
log4j.appender.ErrFileAppender.DatePattern='.' yyyy-MM-dd
log4j.appender.ErrFileAppender.layout.ConversionPattern= %m%n
I figured out the solution.
What I am doing is I am overriding the setFile() method of log4j.FileAppender and there I have added the logic of inserting header only when the file is empty.
This works perfectly. But I am not sure is there any better solution for the same.
Im having the same problem, can you please post the code or an example of the logic you implemented when overriding the setFile method, in order to avoid the insert of header on every restart of the application.
Thanks.
UPDATE I figured our how, here is my implementation:
public class RegisterRollingFileAppender extends DailyRollingFileAppender {
#Override
public synchronized void setFile(String fileName) {
// Your logic goes here
super.setFile(fileName);
File f = new File(super.getFile());
RegisterPatternLayout layout = new RegisterPatternLayout();
if (f.exists()) {
if (f.length() == 0) {
super.setLayout(layout);
} else {
PatternLayout p = new PatternLayout();
p.setConversionPattern("%m%n");
super.setLayout(p);
}
}
}
}
Where RegisterPatternLayout layout = My custom layout class I constructed overriding getHeader() method.
Anyway, I would like to see your implementation, thanks!
Victor's solution is good, but there is one flaw, for example,
if RollingFileAppender is configured, so apparently you want to write header in each of the rolling file. However #override setFile will only insert one header into old file even if old file gets rolled over and new file is created.
I found a solution from this discussion (https://groups.google.com/forum/#!topic/comp.lang.java.programmer/MKl0MT5gePo), its suggestion is to override writeHeader() method. This writeHeader has modifier protected, so we need to create ErrorFile inside org.apache.log4j package
package org.apache.log4j;
public class ErrorFile extends DailyRollingFileAppender {
#Override
protected void writeHeader() {
File f = new File(getFile());
if (f.length() == 0) {
super.writeHeader();
}
}
}
In your log4j.properties, change from
log4j.appender.ErrFileAppender=com.logger.ErrorFile
to
log4j.appender.ErrFileAppender=org.apache.log4j.ErrorFile
I'm trying to develop a little drag & drop application under Java FX. User will drop JFX components like Buttons, Menus, Labels on certain positions. When done, he will save this layout and later on he will reopen the layout and he will use it again.
Its important to store the information about all objects that are dropped on some position.
I decided to use serialization for this purpose. But I'm not able to serialize JavaFX components. I tried to serialize Buttons, Scenes, Stages, JFXPane but nothing seemed to work (I obtained NotSerializableException).
Any suggestions how to save all the components and then retrieve them ?
P.S.: I was trying to find out some method with FXML but I did not succeed.
Thank you very much for your answers :)
You are correct, JavaFX (as of 2.1) does not support serialization of components using the Java Serializable interface - so you cannot use that mechanism.
JavaFX can deserialize from an FXML document using the FXMLLoader.load() method.
The trick though, is how to write your existing components and states out to FXML?
Currently, there is nothing public from the platform which performs FXML serialization. Apparently, creating a generic scenegraph => FXML serializer is quite a complex task (and there is no public 3rd party API for this that I know of). It wouldn't be too difficult to iterate over the scenegraph and write out FXML for a limited set of components and attributes.
If the main goal of saving user components on the servers side - is to have a possibility to show the same interface to the user - why not to save all descriptive information you need about users components, and when it is needed - just rebuild user interface again, using stored descriptive information? Here is primitive example:
/* That is the class for storing information, which you need from your components*/
public class DropedComponentsCoordinates implements Serializable{
private String componentID;
private String x_coord;
private String y_coord;
//and so on, whatever you need to get from yor serializable objects;
//getters and setters are assumed but not typed here.
}
/* I assume a variant with using FXML. If you don't - the main idea does not change*/
public class YourController implements Initializable {
List<DropedComponentsCoordinates> dropedComponentsCoordinates;
#Override
public void initialize(URL url, ResourceBundle rb) {
dropedComponentsCoordinates = new ArrayList();
}
//This function will be fired, every time
//a user has dropped a component on the place he/she wants
public void OnDropFired(ActionEvent event) {
try {
//getting the info we need from components
String componentID = getComponentID(event);
String component_xCoord = getComponent_xCoord(event);
String component_yCoord = getComponent_yCoord(event);
//putting this info to the list
DropedComponentsCoordinates dcc = new DropedComponentsCoordinates();
dcc.setX_Coord(component_xCoord);
dcc.setY_Coord(component_yCoord);
dcc.setComponentID(componentID);
} catch (Exception e) {
e.printStackTrace();
}
}
private String getComponentID(ActionEvent event){
String componentID;
/*getting cpmponentID*/
return componentID;
}
private String getComponent_xCoord(ActionEvent event){
String component_xCoord;
/*getting component_xCoord*/
return component_xCoord;
}
private String getComponent_yCoord(ActionEvent event){
String component_yCoord;
/*getting component_yCoord*/
return component_yCoord;
}
}
Scenario:
I have two classes named ListLayout and GridLayout which both implement CustomLayout.
The user enters a String representing the layout they wish to use ("ListLayout" for example)
How can I create a ListLayout object based on the string entered by the user? I would need to be equivalent to just doing this:
CustomLayout layout = new ListLayout();
Ideally I would need to find a solution which would allow me to check if the String entered corresponds to a predefined class which implements CustomLayout before actually making the object (because it will throw an error if it doesn't exist and I don't check beforehand).
This is really getting me thinking.... thanks in advance for your help
If you don't want to use reflection here, a map of factories could be the right thing:
interface LayoutFactory {
public CustomLayout makeLayout();
}
Map<String, LayoutFactory> factories = new HashMap<String, LayoutFactory>();
factories.put("GridLayout", new LayoutFactory() {
public CustomLayout makeLayout() { return new GridLayout(); }
});
factories.put("ListLayout", new LayoutFactory() {
public CustomLayout makeLayout() { return new ListLayout(); }
});
String layoutName = ...; // user input
CustomLayout l = factories.get(layoutName).makeLayout();
Of course, you also should handle the case where the user did give an unknown layout name (factories.get then returns null).
Do something like this:
String userInput = //get user's input
while(!userInput.equalsIgnoreCase("ListLayout") && !userInput.equalsIgnoreCase("gridLayout")){
System.out.println("Please enter a valid option");
userInput = //get user's input again
}
CustomLayout layout;
if(userInput.equalsIgnoreCase("ListLayout")
layout = new ListLayout();
else
layout = new GridLayout();
I would personally not recommend using reflection; as refactoring becomes a pain.
Better would be to do a string check and instantiate the correct class.
For example
if(userInput.equals("ListLayout")) {
CustomLayout layout = new ListLayout();
} else if (userInput.equals("GridLayout")) {
CustomLayout layout = new GridLayout();
}
This can also be implemented using java reflection as others pointed out. But if you want to refactor the code later on (say using eclipse refactoring for example), then the reflection code will not be auto-refactored. For example, if you used reflection and if you changed the class name of ListLayout to FancyListLayout, and do eclipse auto refactoring, the reflection code will be left untouched and your code will break.
This is the code used to obtain an instance of a class if you have a String with the fully qualified name:
try {
CustomLayout layout = (CustomLayout) Class.forName("your.package.ListLayout").newInstance();
} catch (Exception ex) {
}
The exception can be of type: LinkageError, ExceptionInInitializerError, ClassNotFoundException, IllegalAccessException, InstantiationException and SecurityException and it is recommended to have catch clauses for each of them if you want to handle them differently.
Two steps :
Find if text entered by user corresponds to an existing class (this can be done using the reflections framework, I guess)
Instanciate an object of that class, which is usually done using Class.forName(String)
Ok. What you should be looking into is called Reflection (wiki on reflection) and java offers a rich API for that. THis basically allows you to generate objects from a String and catch the execption if there is no such class accordingly. This has however some drawbacks, please check on the API for further reference.
Cheers!
Reflection is the natural answer, but you could create a Factory for it.
public class CustomLayoutFactory {
public CustomLayout createInstance(String layoutName) {
if("ListLayout".equals(layoutName) {
return new ListLayout();
} else if("GridLayout".equals(layoutName) {
return new GridLayout();
}
return null;
}
}
While not the most elegant solution, it is useful in cases where the SecurityManager is too restrictive for reflection.
Reading all the answers I'd like to strongly recommend against the reflection based ones, since you will have to be very careful in renaming your classes afterwards.
Also instead of:
if ("ListLayout".equals(userInput)) {
return new ListLayout();
}
you can add a protected field inside your base Layout class:
public abstract class Layout {
protected String userInputName;
}
and modifying its extenders like so:
public class ListLayout {
public ListLayout() {
userInputName = "listLayout"; // set protected field
}
}
Then you can do:
for (Layout l : setOfAllLayouts) {
if (userInput.equals(l.getInputName)) {
return l.clone();
}
}
This ought to do it:
Class.forName(userInput).newInstance();
You can then use instanceof to test for what class you ended up with. However, I'd test the value of userInput before doing this, to make sure it's a recognized class name. You might also need to prepend the package name if your users just input the simple class name.
I have a JTextArea always visible in my main app window (a Log if you like), and I want to use it to display activity going on in the system (like mock-debug output you'd do with System.out.println() in if conditions or whatever)
I mean high level things the user does, (like "successfully loaded file " or " written to disk", " completed" etc)
Thing is such messages can be generated anywhere in my system mainly in another package the classes of which deal with the data and computation, and they're unaware of the GUI.
Maybe save the messages to a temp file and the textarea "monitors" that file for changes, how can this be done?
The simplest way is to define a logger interface:
package com.example.logging;
public interface ActivityLogger {
void logAction(String message);
}
Then pass it to your non-GUI components so they don't get tied to a specific implementation:
public class FileLoader {
private ActivityLogger logger;
public FileLoader(ActivityLogger logger){
this.logger = logger;
}
public void loadFile(){
// load stuff from file
logger.logAction("File loaded successfully");
}
}
Now, making an implementation that writes to a text component is simple:
public class TextComponentLogger implements ActivityLogger{
private final JTextComponent target;
public TextComponentLogger(JTextComponent target) {
this.target = target;
}
public void logAction(final String message){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
target.setText(String.format("%s%s%n",
target.getText(),
message));
}
});
}
}
// Usage:
JTextArea logView = new JTextArea();
TextComponentLogger logger = new TextComponentLogger(logView);
FileLoader fileLoader = new FileLoader(logger);
fileLoader.loadFile();
You can of course also use a standard logging framework (java.util.logging, slf4j, log4j, etc) and write an appender that "writes" to a text component.
The design can be rather complicated. Maybe you can have a public access method like updateText() in the class where your TextArea would be. Then you create a kind of 'resource' or 'shared' class (just a plain class) that would be initialized together when your main() runs. When the class containing your TextArea is created, an instance would be placed into the 'shared' class (this shared class should be a singleton) and so all the other classes call this 'shared' class (maybe a method like updateTextArea()) and what it would do is call the class containing the TextArea via that instance and call the TextArea to update text.
The Message Console might be what you are looking for.
Java also has a "Logger" API.
You can use EventBus to decouple your GUI from the other parts of your application. (My blog has another introduction). You could do something as follows:
public class LogArea extends JTextArea {
public static final String LOG_TOPIC = "logarea_topic";
public LogArea() {
super();
// Read in the annotations, register self as a listener to the topic
AnnotationProcessor.process(this);
}
#EventTopicSubscriber(topic=LOG_TOPIC)
public void logEvent(String topic, String text) {
append(text + "\n");
}
}
public class DomainClass {
public void foo() {
// Send out a notification throughout the system to whichever components
// are registered to handle this topic.
EventBus.publish(LogArea.LOG_TOPIC, "some text you want to appear in the log area");
}
}
In a real system you'd probably want to move the topic declarations to another class so that one can use it without being tied to a specific implementation. E.g. you could have a Topics class that just contains the static string constants of the topics. Then you can have multiple classes that listen to those topics and process the messages (e.g. you could have a standard logging framework which writes out to a log file in addition to the jtextarea component).