I am new in log4j. Can anyone explain how to create my own Appender? i.e. how to implement the classes and interfaces and how to override it?
Update: the provided solution is valid for Log4J 1.x . If you're looking for 2.x versions, take a look at this article: How to create a custom appender in log4j2
You should extend AppenderSkeleton class, that (quoting javadoc) "provides the code for common functionality, such as support for threshold filtering and support for general filters."
If you read the code of AppenderSkeleton, you'll see that it handles almost all, leaving to you just:
protected void append(LoggingEvent event)
public void close()
public boolean requiresLayout()
The core method is append. Remember that you don't need to implement the filtering logic in it because it is already implemented in doAppend that in turn calls append.
Here I made a (quite useless) class that stores the log entries in an ArrayList, just as a demo.
public /*static*/ class MyAppender extends AppenderSkeleton {
ArrayList<LoggingEvent> eventsList = new ArrayList();
#Override
protected void append(LoggingEvent event) {
eventsList.add(event);
}
public void close() {
}
public boolean requiresLayout() {
return false;
}
}
Ok, let's test it:
public static void main (String [] args) {
Logger l = Logger.getLogger("test");
MyAppender app = new MyAppender();
l.addAppender(app);
l.warn("first");
l.warn("second");
l.warn("third");
l.trace("fourth shouldn't be printed");
for (LoggingEvent le: app.eventsList) {
System.out.println("***" + le.getMessage());
}
}
You should have "first", "second", "third" printed; the fourth message shouldn't be printed since the log level of root logger is debug while the event level is trace. This proves that AbstractSkeleton implements "level management" correctly for us. So that's definitely seems the way to go... now the question: why do you need a custom appender while there are many built in that log to almost any destination? (btw a good place to start with log4j: http://logging.apache.org/log4j/1.2/manual.html)
If you would like to do some manipulations or decisions you can do it like this:
#Override
protected void append(LoggingEvent event) {
String message = null;
if(event.locationInformationExists()){
StringBuilder formatedMessage = new StringBuilder();
formatedMessage.append(event.getLocationInformation().getClassName());
formatedMessage.append(".");
formatedMessage.append(event.getLocationInformation().getMethodName());
formatedMessage.append(":");
formatedMessage.append(event.getLocationInformation().getLineNumber());
formatedMessage.append(" - ");
formatedMessage.append(event.getMessage().toString());
message = formatedMessage.toString();
}else{
message = event.getMessage().toString();
}
switch(event.getLevel().toInt()){
case Level.INFO_INT:
//your decision
break;
case Level.DEBUG_INT:
//your decision
break;
case Level.ERROR_INT:
//your decision
break;
case Level.WARN_INT:
//your decision
break;
case Level.TRACE_INT:
//your decision
break;
default:
//your decision
break;
}
}
I would like to expend #AgostinoX answer to support pro file configuration and the ability to start and stop the logging capture :
public class StringBufferAppender extends org.apache.log4j.AppenderSkeleton {
StringBuffer logs = new StringBuffer();
AtomicBoolean captureMode = new AtomicBoolean(false);
public void close() {
// TODO Auto-generated method stub
}
public boolean requiresLayout() {
// TODO Auto-generated method stub
return false;
}
#Override
protected void append(LoggingEvent event) {
if(captureMode.get())
logs.append(event.getMessage());
}
public void start()
{
//System.out.println("[StringBufferAppender|start] - Start capturing logs");
StringBuffer logs = new StringBuffer();
captureMode.set(true);
}
public StringBuffer stop()
{
//System.out.println("[StringBufferAppender|start] - Stop capturing logs");
captureMode.set(false);
StringBuffer data = new StringBuffer(logs);
logs = null;
return data;
}
}
Now all you have to do is to define in in the log4j.property file
log4j.rootLogger=...., myAppender # here you adding your appendr name
log4j.appender.myAppender=com.roi.log.StringBufferAppender # pointing it to the implementation
than when ever you want to enable it during runtume:
Logger logger = Logger.getRootLogger();
StringBufferAppender appender = (StringBufferAppender)logger.getAppender("myAppender");
appender.start();
and while want to stop it:
StringBuffer sb = appender.stop();
To create a own Appender you just implement the Appender Interface and just override it.
And also study this link start log
Related
I am learning how to write Minecraft mods (version 1.14.4) and was able to make an item. Now I am trying to make a block. I am following this tutorial video which actually covers 1.14.3, but I thought it would be close enough.
I am currently getting this error:
[20Mar2020 14:09:10.522] [Server thread/INFO] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry Block: Found a missing id from the world examplemod:examplemod
[20Mar2020 14:09:10.613] [Server thread/ERROR] [net.minecraftforge.registries.GameData/REGISTRIES]: Unidentified mapping from registry minecraft:block
examplemod:examplemod: 676
I also get presented with this at runtime:
I have tried messing around with how i'm naming the registries but I just can't seem to pin down what the issue is. Maybe i'm not formatting my json files correctly?
Note that ItemList and BlockList are just classes that contain static instances of each Block/Item I have created.
ExampleMod.java:
// The value here should match an entry in the META-INF/mods.toml file
#Mod(ExampleMod.MOD_ID)
public class ExampleMod
{
// Directly reference a log4j logger.
private static final Logger LOGGER = LogManager.getLogger();
public static final String MOD_ID = "examplemod";
public ExampleMod() {
// Register the setup method for modloading
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup);
// Register the enqueueIMC method for modloading
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::enqueueIMC);
// Register the processIMC method for modloading
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::processIMC);
// Register the doClientStuff method for modloading
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doClientStuff);
// Register ourselves for server and other game events we are interested in
MinecraftForge.EVENT_BUS.register(this);
}
private void setup(final FMLCommonSetupEvent event)
{
// some preinit code
LOGGER.info("HELLO FROM PREINIT");
LOGGER.info("DIRT BLOCK >> {}", Blocks.DIRT.getRegistryName());
}
private void doClientStuff(final FMLClientSetupEvent event) {
// do something that can only be done on the client
LOGGER.info("Got game settings {}", event.getMinecraftSupplier().get().gameSettings);
}
private void enqueueIMC(final InterModEnqueueEvent event)
{
// some example code to dispatch IMC to another mod
InterModComms.sendTo(ExampleMod.MOD_ID, "helloworld", () -> { LOGGER.info("Hello world from the MDK"); return "Hello world";});
}
private void processIMC(final InterModProcessEvent event)
{
// some example code to receive and process InterModComms from other mods
LOGGER.info("Got IMC {}", event.getIMCStream().
map(m->m.getMessageSupplier().get()).
collect(Collectors.toList()));
}
// You can use SubscribeEvent and let the Event Bus discover methods to call
#SubscribeEvent
public void onServerStarting(FMLServerStartingEvent event) {
// do something when the server starts
LOGGER.info("HELLO from server starting");
}
// You can use EventBusSubscriber to automatically subscribe events on the contained class (this is subscribing to the MOD
// Event bus for receiving Registry Events)
#Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD)
public static class RegistryEvents {
#SubscribeEvent
public static void onItemsRegistry(final RegistryEvent.Register<Item> blockItemEvent)
{
ItemList.bomb_item = new Item(new Item.Properties().group(ItemGroup.COMBAT));
ItemList.bomb_item.setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "bomb_item"));
ItemList.mystery_block = new BlockItem(BlockList.mystery_block, new Item.Properties().group(ItemGroup.MISC));
ItemList.mystery_block.setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "mystery_block"));
blockItemEvent.getRegistry().registerAll(ItemList.bomb_item, ItemList.mystery_block);
LOGGER.info("Items registered.");
}
#SubscribeEvent
public static void onBlocksRegistry(final RegistryEvent.Register<Block> blockRegistryEvent) {
BlockList.mystery_block = new Block(Block.Properties.create(Material.CAKE)
.hardnessAndResistance(2.0f, 2.0f)
.sound(SoundType.GLASS));
BlockList.mystery_block.setRegistryName(new ResourceLocation(MOD_ID, "mystery_block"));
blockRegistryEvent.getRegistry().registerAll(BlockList.mystery_block);
LOGGER.info("Blocks registered.");
}
}
}
blockstates/mystery_block.json:
{
"variants": {
"": {
"model": "examplemod:block/mystery_block"
}
}
}
models/block/mystery_block.json:
{
"parent": "block/cube_all",
"textures": {
"all": "examplemod:block/mystery_block"
}
}
models/item/mystery_block.json:
{
"parent": "examplemod:block/mystery_block"
}
All that means is that at some point you had a block registered as "examplemod:examplemod", and now you don't. You can safely ignore the message. If you start a new world instead of opening an old one, you won't get that message.
As an aside, HarryTalks is not a recommended way to learn to mod (on the Minecraft Forge forums ). Apparently he uses several bad practices (I have not actually used them).
Alternative suggestions are Cadiboo's example mod, or McJty's tutorials.
i have joined to one of those Vertx lovers , how ever the single threaded main frame may not be working for me , because in my server there might be 50 file download requests at a moment , as a work around i have created this class
public abstract T onRun() throws Exception;
public abstract void onSuccess(T result);
public abstract void onException();
private static final int poolSize = Runtime.getRuntime().availableProcessors();
private static final long maxExecuteTime = 120000;
private static WorkerExecutor mExecutor;
private static final String BG_THREAD_TAG = "BG_THREAD";
protected RoutingContext ctx;
private boolean isThreadInBackground(){
return Thread.currentThread().getName() != null && Thread.currentThread().getName().equals(BG_THREAD_TAG);
}
//on success will not be called if exception be thrown
public BackgroundExecutor(RoutingContext ctx){
this.ctx = ctx;
if(mExecutor == null){
mExecutor = MyVertxServer.vertx.createSharedWorkerExecutor("my-worker-pool",poolSize,maxExecuteTime);
}
if(!isThreadInBackground()){
/** we are unlocking the lock before res.succeeded , because it might take long and keeps any thread waiting */
mExecutor.executeBlocking(future -> {
try{
Thread.currentThread().setName(BG_THREAD_TAG);
T result = onRun();
future.complete(result);
}catch (Exception e) {
GUI.display(e);
e.printStackTrace();
onException();
future.fail(e);
}
/** false here means they should not be parallel , and will run without order multiple times on same context*/
},false, res -> {
if(res.succeeded()){
onSuccess((T)res.result());
}
});
}else{
GUI.display("AVOIDED DUPLICATE BACKGROUND THREADING");
System.out.println("AVOIDED DUPLICATE BACKGROUND THREADING");
try{
T result = onRun();
onSuccess((T)result);
}catch (Exception e) {
GUI.display(e);
e.printStackTrace();
onException();
}
}
}
allowing the handlers to extend it and use it like this
public abstract class DefaultFileHandler implements MyHttpHandler{
public abstract File getFile(String suffix);
#Override
public void Handle(RoutingContext ctx, VertxUtils utils, String suffix) {
new BackgroundExecutor<Void>(ctx) {
#Override
public Void onRun() throws Exception {
File file = getFile(URLDecoder.decode(suffix, "UTF-8"));
if(file == null || !file.exists()){
utils.sendResponseAndEnd(ctx.response(),404);
return null;
}else{
utils.sendFile(ctx, file);
}
return null;
}
#Override
public void onSuccess(Void result) {}
#Override
public void onException() {
utils.sendResponseAndEnd(ctx.response(),404);
}
};
}
and here is how i initialize my vertx server
vertx.deployVerticle(MainDeployment.class.getCanonicalName(),res -> {
if (res.succeeded()) {
GUI.display("Deployed");
} else {
res.cause().printStackTrace();
}
});
server.requestHandler(router::accept).listen(port);
and here is my MainDeployment class
public class MainDeployment extends AbstractVerticle{
#Override
public void start() throws Exception {
// Different ways of deploying verticles
// Deploy a verticle and don't wait for it to start
for(Entry<String, MyHttpHandler> entry : MyVertxServer.map.entrySet()){
MyVertxServer.router.route(entry.getKey()).handler(new Handler<RoutingContext>() {
#Override
public void handle(RoutingContext ctx) {
String[] handlerID = ctx.request().uri().split(ctx.currentRoute().getPath());
String suffix = handlerID.length > 1 ? handlerID[1] : null;
entry.getValue().Handle(ctx, new VertxUtils(), suffix);
}
});
}
}
}
this is working just fine when and where i need it , but i still wonder if is there any better way to handle concurencies like this on vertx , if so an example would be really appreciated . thanks alot
I don't fully understand your problem and reasons for your solution. Why don't you implement one verticle to handle your http uploads and deploy it multiple times? I think that handling 50 concurrent uploads should be a piece of cake for vert.x.
When deploying a verticle using a verticle name, you can specify the number of verticle instances that you want to deploy:
DeploymentOptions options = new DeploymentOptions().setInstances(16);
vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", options);
This is useful for scaling easily across multiple cores. For example you might have a web-server verticle to deploy and multiple cores on your machine, so you want to deploy multiple instances to take utilise all the cores.
http://vertx.io/docs/vertx-core/java/#_specifying_number_of_verticle_instances
vertx is a well-designed model so that a concurrency issue does not occur.
generally, vertx does not recommend the multi-thread model.
(because, handling is not easy.)
If you select multi-thread model, you have to think about shared data..
Simply, if you just only want to split EventLoop Area,
first of all, you make sure Check your a number of CPU Cores.
and then Set up the count of Instances .
DeploymentOptions options = new DeploymentOptions().setInstances(4);
vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", options);
But, If you have 4cores of CPU, you don't set up over 4 instances.
If you set up to number four or more, the performance won't improve.
vertx concurrency reference
http://vertx.io/docs/vertx-core/java/
I have created a common logger in the following manner in a root class that is referenced by many sub classes.
public static final Logger LOGGER = Logger.getLogger(RootClass.class.getName());
Then I am accessing that logger in child classes in the following manner.
private static final Logger LOGGER = RootClass.LOGGER;
I have a main classes which receives inputs from users and it calls a method in the RootClass to configure the logger.
logStatus = messageBus.configureLogPath(logPath,logLevel);
Below is the implementation of the above method.
public Boolean configureLogPath(String logPath,String level) {
Boolean result=false;
switch(level) {
case "info" :
LOGGER.setLevel(Level.INFO);
break;
case "severe":
LOGGER.setLevel(Level.SEVERE);
break;
case "debug":
LOGGER.setLevel(Level.CONFIG);
break;
case "off" :
LOGGER.setLevel(Level.OFF);
break;
default :
LOGGER.setLevel(Level.SEVERE);
}
try {
simpleFormatter = new SimpleFormatter();
logFileHandler = new FileHandler(logPath);
logFileHandler.setFormatter(simpleFormatter);
LOGGER.addHandler(logFileHandler);
result =true;
} catch (SecurityException e1) {
result= false;
LOGGER.log(Level.SEVERE, "Security exception when reading log file" + e1);
} catch (IOException e1) {
result = false;
LOGGER.log(Level.SEVERE, "IO Exception when reading log file" + e1);
}
return result;
}
Is it possible for me to disable logging from certain selected child classes? I have a requirement where the user should be able to set certain child classes for which the logs should be created. But since I am using a common logger in all the child classes I am not sure how to achieve this. Please advice.
To meet your requirements you have to create a custom log filter that checks the source class name and install it on the root logger.
public class SourceFilter implements Filter {
#Override
public boolean isLoggable(LogRecord record) {
//Source class can return null so handle it.
String cn = String.valueOf(record.getSourceClassName());
return !cn.startsWith("foo.bar.Child1")
&& !cn.startsWith("foo.bar.Child2")
&& !cn.startsWith("foo.bar.Child3");
}
}
Also, the level class has a parse method you can use to get rid of that switch statement.
We can mention the below code for logging off from particular Java Classes as:
Logger.getLogger("cs.fid.tm.reportingService.config.test1").setLevel(Level.OFF);
If all else fails, you could always just walk the call stack and see where your function was called from. Perhaps not the most efficient solution, but it should work in your case.
I am new to Apache Camel, I have written a simple route to scan a directory (/test), file will be processed when it was copied into the directory. Anyone has an idea on how to write a camel unit test to test the following route? Is there a way to mock the process of copying the file into the /test directory so that the route will be triggered.
public void configure() {
from( "file:/test?preMove=IN_PROGRESS" +
"&move=completed/${date:now:yyyyMMdd}/${file:name}" +
"&moveFailed=FAILED/${file:name.noext}-${date:now:yyyyMMddHHmmssSSS}.${file:ext}" )
.process(new Processor() {
public void process(Exchange exchange) throws IOException {
File file = (File) exchange.getIn().getBody();
// read file content ......
}
});
}
You have done the routing by one of many correct ways. But there exist some more important pieces to make your code run - you should create a context, create a router with this your configure(), add it to a context, and run this context.
Sorry, I prefer beans to processors, so you have also to register a bean. And make you processing a normal named method in a named class.
I think, the most compact info is here. JUnit test is a standalone app and you need to run Camel as a standalone app for JUnit testing.
I think the basic idea is that you mock the end endpoint so you can check what is coming out your route. There are a few different ways, but you could test your route as follows:
public class MyRouteTest extends CamelSpringTestSupport {
private static final String INPUT_FILE = "myInputFile.xml";
private static final String URI_START = "direct:start";
private static final String URI_END = "mock:end";
#Override
public boolean isUseAdviceWith() {
return true;
}
#Override
protected AbstractApplicationContext createApplicationContext() {
return new AnnotationConfigApplicationContext(CamelTestConfig.class); // this is my Spring test config, where you wire beans
}
#Override
protected RouteBuilder createRouteBuilder() {
MyRoute route = new MyRoute();
route.setFrom(URI_START); // I have added getter and setters to MyRoute so I can mock 'start' and 'end'
route.setTo(URI_END);
return route;
}
#Test
public void testMyRoute() throws Exception {
MockEndpoint result = getMockEndpoint(URI_END);
context.start();
// I am just checking I receive 5 messages, but you should actually check the content with expectedBodiesReceived() depending on what your processor does to the those files.
result.expectedMessageCount(5);
// I am just sending the same file 5 times
for (int i = 0; i < 5; i++) {
template.sendBody(URI_START, getInputFile(INPUT_FILE));
}
result.assertIsSatisfied();
context.stop();
}
private File getInputFile(String name) throws URISyntaxException, IOException {
return FileUtils.getFile("src", "test", "resources", name);
}
I am sure you already solved your issue is 2013, but this is how I would solve it in 2017. Regards
I need to hack up a small tool. It should read a couple of files and convert them. Right now that works in my IDE. For the user, I'd like to add a small UI which simply shows the log output.
Do you know of a ready-to-use Swing appender for logback? Or something which redirects System.out to a little UI with nothing more than a text field and a "Close" button?
PS: I'm not looking for Chainsaw or Jigsaw or Lilith. I want the display of the log messages in the application, please.
You need to write a custom appender class like so:
public class MyConsoleAppender extends AppenderBase<ILoggingEvent> {
private Encoder<ILoggingEvent> encoder = new EchoEncoder<ILoggingEvent>();
private ByteArrayOutputStream out = new ByteArrayOutputStream();
public MyConsoleAppender() {
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
setContext(lc);
start();
lc.getLogger("ROOT").addAppender(this);
}
#Override
public void start() {
try {
encoder.init(out);
} catch (IOException e) {}
super.start();
}
#Override
public void append(ILoggingEvent event) {
try {
encoder.doEncode(event);
out.flush();
String line = out.toString(); // TODO: append _line_ to your JTextPane
out.reset();
} catch (IOException e) {}
}
}
You can replace the EchoEncoder with a PatternLayoutEncoder (see CountingConsoleAppender example in the logback examples folder).
The encoder will write each event to a byte buffer, which you can then extract a string and write this to your JTextPane or JTextArea, or whatever you want.
I often rely on JTextArea#append(), as suggested in this example. Unlike most of Swing, the method happens to be thread safe.
Addendum: Console is a related example that redirects System.out and System.err to a JTextArea.
No warranty, but here's a sample that I just wrote:
/**
* A Logback appender that appends messages to a {#link JTextArea}.
* #author David Tombs
*/
public class JTextAreaAppender extends AppenderBase<ILoggingEvent>
{
private final JTextArea fTextArea;
private final PatternLayout fPatternLayout;
public JTextAreaAppender(final Context loggerContext, final JTextArea textArea)
{
fTextArea = textArea;
// Log the date, level, class name (no package), and the message.
fPatternLayout = new PatternLayout();
fPatternLayout.setPattern("%d{HH:mm:ss.SSS} %-5level - %msg");
fPatternLayout.setContext(loggerContext);
fPatternLayout.start();
// Make sure not to call any subclass methods right now.
super.setContext(loggerContext);
}
#Override
protected void append(final ILoggingEvent eventObject)
{
// Actual appending must be done from the EDT.
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run()
{
final String logStr = fPatternLayout.doLayout(eventObject);
// If the text area already has lines in it, append a newline first.
if (fTextArea.getDocument().getLength() > 0)
{
fTextArea.append("\n" + logStr);
}
else
{
fTextArea.setText(logStr);
}
}
});
}
}