문제

I'm quite confused on how to properly setup a logger in an Java EE OSGi environment. Here are my requirements:

  • Only 1 log file per EBA (grouping of bundles)
  • Multiple log files per application server (due to multiple EBAs)
  • Do not want to perform ClassLoader magic (its fine if a library I use does this, I just don't want to have to write it)
  • Must rotate the log file at the end of the day and only maintain 7 log files at once
  • Preferably does not require creating a logging bundle per EBA. No other developer will buy in if I tell them to write their own logging interop for each application.
  • Must work with WebSphere Application Server 8.5.5

First I tried to use SLF4j on top of log4j like our other Java EE applications do, however nothing could find my log4j.properties. I tried variations of importing SLF4j, and also had issues where loading it in 1 bundle prevented it from loading in another.

Next I looked into PAX logger, but it appears to log globally, not per EBA.

Attempting to use the OSGi LogService prevents my bundle from deploying to WebSphere, plus I'm not sure how I could get it to meet my requirements anyway.

I'm at the point where the only option I can see is to write my own bundle that keeps a registry for bundle -> log file (using FrameworkUtil.getBundle on the client class) and implement a full logging framework within it. If that has classloader isolation issues then possibly push to an EJB to do the actual logging. I'm really hoping that's not my only solution.

Can anyone point me to some documentation that will help me out??

Thank you!

도움이 되었습니까?

해결책

People are in general confused about the Log Service ... The Log Service does not store any logs, it just acts as a dispatcher. I understand the confusion since the Log Service is mandated to have a small buffer for the initial start up and provides an API to get the buffer.

For what you want, you should add a Log Listener with the Log Reader services. What you want is quite easy with Declarative Services. This is a component that implements your requirements :

@Component(provide = {}, immediate = true) public class Logger extends Thread
    implements LogListener {
  final BlockingQueue<LogEntry> queue = new ArrayBlockingQueue<LogEntry>(1000);
  File root;

  @Reference void setLR(LogReaderService lr) {
    lr.addLogListener(this);
  }

  @Activate void activate(BundleContext context, Map<String,Object> props) {
    if ( props.containsKey("root"))
      root = new File((String) props.get("root"));
    else
      root = context.getDataFile("logs");
    root.mkdirs();
    start();
  }

  @Deactivate void deactivate() {
    interrupt();
  }

  @Override public void logged(LogEntry entry) {
    queue.offer(entry); // ignore full silently
  }

  public void run() {
    while (true)
      try {
        LogEntry entry = queue.take();
        File file = getPath(entry);

        if (file.isFile()) {
          long days = TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis()
              - file.lastModified());
          if (days > 2) file.delete();
        }

        try (OutputStream raf = new FileOutputStream(file, true)) {
          String s = String.format("%tT [%03d] %s%n", entry.getTime(), entry
              .getBundle().getBundleId(), entry.getMessage());
          raf.write(s.getBytes("UTF-8"));
        }

      } catch (InterruptedException ie) {
        return;
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
  }

  private File getPath(LogEntry entry) {
    long rollover = TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis()) % 7;
    String eba = "eba"; // fill in eba from entry.getBundle()?
    return new File(root, "log-" + eba + "-" + rollover + ".msg");
  }
}

This could of course be done a bit more efficient, but that is left as an exercise.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top