Skip to content

Commit

Permalink
Option to send Equinox Framework trace to the OSGi LogService
Browse files Browse the repository at this point in the history
The trace messages in the framework are currently sent to the
System.out.  This commit adds an option to send the Framework
trace messages to the OSGi LogService using the LogLevel
of Trace.  The Logger name used is the trace option key.

To set Equinox trace options with LoggerAdmin the logger
name EQUINOX.TRACE is set to LogLevel TRACE. When EQUINOX.TRACE
logger is set to TRACE then all other logger names in the
LoggerContext configuration set to TRACE are also assumed to
be trace names to be enabled.
  • Loading branch information
tjwatson authored and laeubi committed Jan 25, 2025
1 parent 7461945 commit 8f4fe61
Show file tree
Hide file tree
Showing 47 changed files with 827 additions and 601 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
@RunWith(Suite.class)
@Suite.SuiteClasses({ //
AllLogServiceTests.class, //
AllExtendedLogServiceTests.class //
AllExtendedLogServiceTests.class, //
LogEquinoxTraceTest.class, //
})
public class AllTests {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/*******************************************************************************
* Copyright (c) 2007, 2022 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors: IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.log.test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.equinox.log.SynchronousLogListener;
import org.eclipse.osgi.internal.debug.Debug;
import org.eclipse.osgi.launch.Equinox;
import org.eclipse.osgi.service.debug.DebugOptions;
import org.eclipse.osgi.tests.OSGiTestsActivator;
import org.eclipse.osgi.tests.bundles.AbstractBundleTests;
import org.junit.Test;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.condition.Condition;
import org.osgi.service.log.LogEntry;
import org.osgi.service.log.LogLevel;
import org.osgi.service.log.LogReaderService;
import org.osgi.service.log.Logger;
import org.osgi.service.log.admin.LoggerAdmin;
import org.osgi.service.log.admin.LoggerContext;
import org.osgi.service.startlevel.StartLevel;

@SuppressWarnings("deprecation") // LogService
public class LogEquinoxTraceTest extends AbstractBundleTests {

static class TestListener implements SynchronousLogListener {
List<LogEntry> logs = new ArrayList<>();

@Override
public void logged(LogEntry entry) {
synchronized (logs) {
Bundle b = entry.getBundle();
if (b != null && b.getBundleId() == 0) {
logs.add(entry);
}
}
}

List<LogEntry> getLogs() {
synchronized (logs) {
List<LogEntry> results = new ArrayList(logs);
logs.clear();
return results;
}
}
}

private LoggerAdmin loggerAdmin = null;
private TestListener testListener = null;
private Equinox equinox = null;

@Override
public void setUp() throws Exception {
File config = OSGiTestsActivator.getContext().getDataFile(getName());
Map<String, Object> configuration = new HashMap<>();
configuration.put(Constants.FRAMEWORK_STORAGE, config.getAbsolutePath());
equinox = new Equinox(configuration);
equinox.start();
loggerAdmin = equinox.getBundleContext()
.getService(equinox.getBundleContext().getServiceReference(LoggerAdmin.class));
LogReaderService logReader = equinox.getBundleContext()
.getService(equinox.getBundleContext().getServiceReference(LogReaderService.class));

testListener = new TestListener();
logReader.addLogListener(testListener);
}

@Override
public void tearDown() {
if (equinox != null) {
try {
equinox.stop();
} catch (Exception e) {
// ignore
}
}
}

@Test
public void testEnableTrace() {
BundleContext systemContext = equinox.getBundleContext();
DebugOptions debugOptions = systemContext.getService(systemContext.getServiceReference(DebugOptions.class));
assertFalse("Expected debug options to be disabled.", debugOptions.isDebugEnabled());
assertEquals("Expected no debug Options.", 0, debugOptions.getOptions().size());

LoggerContext rootContext = loggerAdmin.getLoggerContext(null);
Map<String, LogLevel> rootLogLevels = rootContext.getLogLevels();

// enable service trace
rootLogLevels.put(Debug.EQUINOX_TRACE, LogLevel.TRACE);
rootLogLevels.put(Debug.OPTION_DEBUG_SERVICES, LogLevel.TRACE);
rootContext.setLogLevels(rootLogLevels);

assertTrue("Expected debug to be enabled.", debugOptions.isDebugEnabled());
assertEquals("Expected 1 debug Option.", 1, debugOptions.getOptions().size());
assertTrue("Expected trace option to be enabled.",
debugOptions.getBooleanOption(Debug.OPTION_DEBUG_SERVICES, false));
assertFalse("Expected trace option to be disabled.",
debugOptions.getBooleanOption(Debug.OPTION_DEBUG_STARTLEVEL, false));

// get some service to generate trace
ServiceReference<StartLevel> ref = equinox.getBundleContext().getServiceReference(StartLevel.class);
List<LogEntry> traceLogs = testListener.getLogs();
assertNotEquals("Expected to have some trace logs.", 0, traceLogs.size());
for (LogEntry logEntry : traceLogs) {
assertEquals("Wrong logger name", Debug.OPTION_DEBUG_SERVICES,
logEntry.getLoggerName());
}

// disable service trace, enable startlevel trace
rootLogLevels.remove(Debug.OPTION_DEBUG_SERVICES);
rootLogLevels.put(Debug.OPTION_DEBUG_STARTLEVEL, LogLevel.TRACE);
rootContext.setLogLevels(rootLogLevels);

assertTrue("Expected debug to be enabled.", debugOptions.isDebugEnabled());
assertEquals("Expected 1 debug Option.", 1, debugOptions.getOptions().size());
assertTrue("Expected trace option to be enabled.",
debugOptions.getBooleanOption(Debug.OPTION_DEBUG_STARTLEVEL, false));
assertFalse("Expected trace option to be disabled.",
debugOptions.getBooleanOption(Debug.OPTION_DEBUG_SERVICES, false));

// Get the StartLevel service to generate service logs (should be disabled)
// Use the StartLevel service to generate startlevel logs (should be enabled)
StartLevel startLevel = equinox.getBundleContext().getService(ref);
startLevel.setStartLevel(20);

traceLogs = testListener.getLogs();
assertNotEquals("Expected to have some trace logs.", 0, traceLogs.size());
for (LogEntry logEntry : traceLogs) {
assertEquals("Wrong logger name", Debug.OPTION_DEBUG_STARTLEVEL,
logEntry.getLoggerName());
}

// remove all debug options which should disable debug trace
rootLogLevels.clear();
rootLogLevels.put(Debug.EQUINOX_TRACE, LogLevel.TRACE);
rootContext.setLogLevels(rootLogLevels);
assertFalse("Expected debug to be disabled.", debugOptions.isDebugEnabled());
assertEquals("Expected no debug Options.", 0, debugOptions.getOptions().size());
}

@Test
public void testRootLoggerTrace() {
LoggerContext rootContext = loggerAdmin.getLoggerContext(null);
Map<String, LogLevel> rootLogLevels = rootContext.getLogLevels();

// make sure that enabling the root logger trace does not enable all Equinox
// trace
rootLogLevels.put(Logger.ROOT_LOGGER_NAME, LogLevel.TRACE);
// enable some unused trace for equinox
rootLogLevels.put(Debug.EQUINOX_TRACE, LogLevel.TRACE);
rootLogLevels.put(Debug.OPTION_DEBUG_STARTLEVEL, LogLevel.TRACE);
rootContext.setLogLevels(rootLogLevels);

// get some service to generate trace
equinox.getBundleContext().getServiceReference(Condition.class);
List<LogEntry> traceLogs = testListener.getLogs();
assertEquals("Did not expect any trace logs.", 0, traceLogs.size());
}

@Test
public void testLoaderTrace() {
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
Expand Down Expand Up @@ -82,7 +81,6 @@
import org.eclipse.osgi.container.namespaces.EclipsePlatformNamespace;
import org.eclipse.osgi.container.namespaces.EquinoxModuleDataNamespace;
import org.eclipse.osgi.framework.util.ThreadInfoReport;
import org.eclipse.osgi.internal.debug.Debug;
import org.eclipse.osgi.internal.framework.EquinoxConfiguration;
import org.eclipse.osgi.report.resolution.ResolutionReport;
import org.eclipse.osgi.tests.container.dummys.DummyCollisionHook;
Expand Down Expand Up @@ -1879,20 +1877,15 @@ public void testUses1Dynamic() throws BundleException, IOException {
ModuleWire dynamicWire = container.resolveDynamic("uses1", uses_c_dynamic.getCurrentRevision());
assertNotNull("No dynamic wire.", dynamicWire);

PrintStream originalOut = Debug.out;
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
PrintStream testOut = new PrintStream(bytesOut);
Debug.out = testOut;
try {
dynamicWire = container.resolveDynamic("uses2", uses_c_dynamic.getCurrentRevision());
assertNull("Dynamic wire found.", dynamicWire);
} finally {
Debug.out = originalOut;
testOut.close();
}
String traceOutput = bytesOut.toString();
assertTrue("Wrong traceOutput: " + traceOutput,
traceOutput.startsWith("org.apache.felix.resolver.reason.ReasonException"));
dynamicWire = container.resolveDynamic("uses2", uses_c_dynamic.getCurrentRevision());
assertNull("Dynamic wire found.", dynamicWire);

List<String> messages = adaptor.getTraceMessages();
assertEquals("Did not expect messages", 0, messages.size());
List<Throwable> throwables = adaptor.getTraceThrowables();
assertEquals("Expected a resolution exception.", 1, throwables.size());
assertEquals("Expected a resolution exception.", "org.apache.felix.resolver.reason.ReasonException",
throwables.get(0).getClass().getName());
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@
*******************************************************************************/
package org.eclipse.osgi.tests.container.dummys;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
Expand Down Expand Up @@ -44,6 +48,8 @@ public class DummyContainerAdaptor extends ModuleContainerAdaptor {
private final DebugOptions debugOptions;
private final AtomicReference<CountDownLatch> startLatch = new AtomicReference<>();
private final AtomicReference<CountDownLatch> stopLatch = new AtomicReference<>();
private final Queue<String> traceMessages = new ConcurrentLinkedQueue<>();
private final Queue<Throwable> traceThrowables = new ConcurrentLinkedQueue<>();
private volatile Executor resolverExecutor;
private volatile ScheduledExecutorService timeoutExecutor;

Expand Down Expand Up @@ -170,4 +176,21 @@ public void setStopLatch(CountDownLatch stopLatch) {
this.stopLatch.set(stopLatch);
}

@Override
public void trace(String topic, String message) {
traceMessages.add(message);
}

@Override
public void traceThrowable(String topic, Throwable t) {
traceThrowables.add(t);
}

public List<String> getTraceMessages() {
return new ArrayList<>(traceMessages);
}

public List<Throwable> getTraceThrowables() {
return new ArrayList<>(traceThrowables);
}
}
4 changes: 0 additions & 4 deletions bundles/org.eclipse.osgi/.options
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ org.eclipse.osgi/debug/loader/cds=false
org.eclipse.osgi/debug/events=false
# Prints out OSGi service debug information (registration/getting/ungetting etc.)
org.eclipse.osgi/debug/services=false
# Prints out bundle manifest parsing debug information
org.eclipse.osgi/debug/manifest=false
# Prints out LDAP filter debug information
org.eclipse.osgi/debug/filter=false
# Prints out security (PermissionAdmin service) debug information
org.eclipse.osgi/debug/security=false
# Prints out start level service debug information
Expand Down
2 changes: 1 addition & 1 deletion bundles/org.eclipse.osgi/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Export-Package: org.eclipse.core.runtime.adaptor;x-friends:="org.eclipse.core.runtime",
org.eclipse.core.runtime.internal.adaptor;x-internal:=true,
org.eclipse.equinox.log;version="1.1";uses:="org.osgi.framework,org.osgi.service.log",
org.eclipse.osgi.container;version="1.8.0";
org.eclipse.osgi.container;version="1.9.0";
uses:="org.eclipse.osgi.report.resolution,
org.osgi.framework.wiring,
org.eclipse.osgi.framework.eventmgr,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,31 @@
*******************************************************************************/
package org.eclipse.core.runtime.adaptor;

import java.io.*;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import org.eclipse.core.runtime.internal.adaptor.*;
import org.eclipse.core.runtime.internal.adaptor.ConsoleManager;
import org.eclipse.core.runtime.internal.adaptor.DefaultStartupMonitor;
import org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher;
import org.eclipse.osgi.container.Module;
import org.eclipse.osgi.container.ModuleRevision;
import org.eclipse.osgi.container.namespaces.EquinoxModuleDataNamespace;
Expand All @@ -44,7 +60,18 @@
import org.eclipse.osgi.storage.url.reference.Handler;
import org.eclipse.osgi.util.ManifestElement;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.*;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleException;
import org.osgi.framework.BundleListener;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.startlevel.BundleStartLevel;
import org.osgi.framework.startlevel.FrameworkStartLevel;
Expand Down Expand Up @@ -397,8 +424,9 @@ private static int getStartLevel() {
try {
return Integer.parseInt(level);
} catch (NumberFormatException e) {
if (debug)
if (debug) {
Debug.println("Start level = " + level + " parsed. Using hardcoded default: 6"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
return DEFAULT_INITIAL_STARTLEVEL;
}
Expand Down Expand Up @@ -655,8 +683,9 @@ private static Bundle[] loadBasicBundles() throws InterruptedException {
Bundle[] lazyInitBundles = lazyActivationBundles.toArray(new Bundle[lazyActivationBundles.size()]);
startBundles(startInitBundles, lazyInitBundles);

if (debug)
if (debug) {
Debug.println("Time to load bundles: " + (System.currentTimeMillis() - startTime)); //$NON-NLS-1$
}
return startInitBundles;
}

Expand Down
Loading

0 comments on commit 8f4fe61

Please sign in to comment.