package com.oracle.truffle.api.test.polyglot;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.InstrumentInfo;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleContext;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.TruffleLogger;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.TruffleInstrument;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.test.GCUtils;
import java.io.File;
import java.lang.reflect.Field;
import java.nio.file.Files;
import org.junit.Assume;
public class LoggingTest {
@After
public void tearDown() {
AbstractLoggingLanguage.action = null;
}
@Test
public void testDefaultLogging() {
final TestHandler handler = new TestHandler();
final Level defaultLevel = Level.INFO;
try (Context ctx = newContextBuilder().logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.emptyMap());
Assert.assertEquals(expected, handler.getLog());
handler.clear();
ctx.eval(LoggingLanguageSecond.ID, "");
expected = createExpectedLog(LoggingLanguageSecond.ID, defaultLevel, Collections.emptyMap());
Assert.assertEquals(expected, handler.getLog());
}
}
@Test
public void testSingleLanguageAllLogging() {
final Level defaultLevel = Level.INFO;
TestHandler handler = new TestHandler();
try (Context ctx = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINEST.toString())).logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
ctx.eval(LoggingLanguageSecond.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.FINEST, Collections.emptyMap()));
expected.addAll(createExpectedLog(LoggingLanguageSecond.ID, defaultLevel, Collections.emptyMap()));
Assert.assertEquals(expected, handler.getLog());
}
handler = new TestHandler();
try (Context ctx = newContextBuilder().options(createLoggingOptions(LoggingLanguageSecond.ID, null, Level.FINEST.toString())).logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
ctx.eval(LoggingLanguageSecond.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.emptyMap()));
expected.addAll(createExpectedLog(LoggingLanguageSecond.ID, Level.FINEST, Collections.emptyMap()));
Assert.assertEquals(expected, handler.getLog());
}
}
@Test
public void testAllLanguagesAllLogging() {
TestHandler handler = new TestHandler();
try (Context ctx = newContextBuilder().options(createLoggingOptions(null, null, Level.FINEST.toString())).logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
ctx.eval(LoggingLanguageSecond.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.FINEST, Collections.emptyMap()));
expected.addAll(createExpectedLog(LoggingLanguageSecond.ID, Level.FINEST, Collections.emptyMap()));
Assert.assertEquals(expected, handler.getLog());
}
}
@Test
public void testBothLanguagesAllLogging() {
TestHandler handler = new TestHandler();
try (Context ctx = newContextBuilder().options(
createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINEST.toString(), LoggingLanguageSecond.ID, null, Level.FINEST.toString())).logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
ctx.eval(LoggingLanguageSecond.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.FINEST, Collections.emptyMap()));
expected.addAll(createExpectedLog(LoggingLanguageSecond.ID, Level.FINEST, Collections.emptyMap()));
Assert.assertEquals(expected, handler.getLog());
}
}
@Test
public void testFinestOnListLogger() {
final Level defaultLevel = Level.INFO;
TestHandler handler = new TestHandler();
try (Context ctx = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, "a.b", Level.FINEST.toString())).logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
ctx.eval(LoggingLanguageSecond.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.singletonMap("a.b", Level.FINEST)));
expected.addAll(createExpectedLog(LoggingLanguageSecond.ID, defaultLevel, Collections.emptyMap()));
Assert.assertEquals(expected, handler.getLog());
}
}
@Test
public void testFinestOnIntermediateLogger() {
final Level defaultLevel = Level.INFO;
TestHandler handler = new TestHandler();
try (Context ctx = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, "a.a", Level.FINEST.toString())).logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
ctx.eval(LoggingLanguageSecond.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.singletonMap("a.a", Level.FINEST)));
expected.addAll(createExpectedLog(LoggingLanguageSecond.ID, defaultLevel, Collections.emptyMap()));
Assert.assertEquals(expected, handler.getLog());
}
}
@Test
public void testFinestOnIntermediateNonExistentLogger() {
final Level defaultLevel = Level.INFO;
TestHandler handler = new TestHandler();
try (Context ctx = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, "b.a.a", Level.FINEST.toString())).logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
ctx.eval(LoggingLanguageSecond.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.singletonMap("b.a.a", Level.FINEST)));
expected.addAll(createExpectedLog(LoggingLanguageSecond.ID, defaultLevel, Collections.emptyMap()));
Assert.assertEquals(expected, handler.getLog());
}
}
@Test
public void testDifferentLogLevelOnChildAndParent() {
final Level defaultLevel = Level.INFO;
TestHandler handler = new TestHandler();
try (Context ctx = newContextBuilder().options(createLoggingOptions(
LoggingLanguageFirst.ID, "a", Level.FINE.toString(),
LoggingLanguageFirst.ID, "a.a", Level.FINER.toString(),
LoggingLanguageFirst.ID, "a.a.a", Level.FINEST.toString())).logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
ctx.eval(LoggingLanguageSecond.ID, "");
final List<Map.Entry<Level, String>> expected = new ArrayList<>();
final Map<String, Level> levels = new HashMap<>();
levels.put("a", Level.FINE);
levels.put("a.a", Level.FINER);
levels.put("a.a.a", Level.FINEST);
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, levels));
expected.addAll(createExpectedLog(LoggingLanguageSecond.ID, defaultLevel, Collections.emptyMap()));
Assert.assertEquals(expected, handler.getLog());
}
}
@Test
public void testMultipleContextsExclusive() {
final Level defaultLevel = Level.INFO;
TestHandler handler = new TestHandler();
try (Context ctx = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, "a.a", Level.FINEST.toString())).logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
ctx.eval(LoggingLanguageSecond.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.singletonMap("a.a", Level.FINEST)));
expected.addAll(createExpectedLog(LoggingLanguageSecond.ID, defaultLevel, Collections.emptyMap()));
Assert.assertEquals(expected, handler.getLog());
}
handler = new TestHandler();
try (Context ctx = newContextBuilder().options(createLoggingOptions(LoggingLanguageSecond.ID, "a.a", Level.FINEST.toString())).logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
ctx.eval(LoggingLanguageSecond.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.emptyMap()));
expected.addAll(createExpectedLog(LoggingLanguageSecond.ID, defaultLevel, Collections.singletonMap("a.a", Level.FINEST)));
Assert.assertEquals(expected, handler.getLog());
}
handler = new TestHandler();
try (Context ctx = newContextBuilder().logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
ctx.eval(LoggingLanguageSecond.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.emptyMap()));
expected.addAll(createExpectedLog(LoggingLanguageSecond.ID, defaultLevel, Collections.emptyMap()));
Assert.assertEquals(expected, handler.getLog());
}
}
@Test
public void testMultipleContextsNested() {
final Level defaultLevel = Level.INFO;
final TestHandler handler1 = new TestHandler();
final TestHandler handler2 = new TestHandler();
final TestHandler handler3 = new TestHandler();
try (Context ctx1 = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, "a.a", Level.FINEST.toString())).logHandler(handler1).build()) {
try (Context ctx2 = newContextBuilder().options(createLoggingOptions(LoggingLanguageSecond.ID, "a.a", Level.FINEST.toString())).logHandler(handler2).build()) {
try (Context ctx3 = newContextBuilder().logHandler(handler3).build()) {
ctx1.eval(LoggingLanguageFirst.ID, "");
ctx1.eval(LoggingLanguageSecond.ID, "");
ctx2.eval(LoggingLanguageFirst.ID, "");
ctx2.eval(LoggingLanguageSecond.ID, "");
ctx3.eval(LoggingLanguageFirst.ID, "");
ctx3.eval(LoggingLanguageSecond.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.singletonMap("a.a", Level.FINEST)));
expected.addAll(createExpectedLog(LoggingLanguageSecond.ID, defaultLevel, Collections.emptyMap()));
Assert.assertEquals(expected, handler1.getLog());
expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.emptyMap()));
expected.addAll(createExpectedLog(LoggingLanguageSecond.ID, defaultLevel, Collections.singletonMap("a.a", Level.FINEST)));
Assert.assertEquals(expected, handler2.getLog());
expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.emptyMap()));
expected.addAll(createExpectedLog(LoggingLanguageSecond.ID, defaultLevel, Collections.emptyMap()));
Assert.assertEquals(expected, handler3.getLog());
}
}
}
}
@Test
public void testMultipleContextsNested2() {
final Level defaultLevel = Level.INFO;
final TestHandler handler1 = new TestHandler();
final TestHandler handler2 = new TestHandler();
try (Context ctx1 = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, "a", Level.FINER.toString())).logHandler(handler1).build()) {
try (Context ctx2 = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, "a.a", Level.FINE.toString())).logHandler(handler2).build()) {
ctx1.eval(LoggingLanguageFirst.ID, "");
ctx2.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.singletonMap("a", Level.FINER)));
Assert.assertEquals(expected, handler1.getLog());
expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.singletonMap("a.a", Level.FINE)));
Assert.assertEquals(expected, handler2.getLog());
}
}
}
@Test
public void testMultipleContextsNested3() {
final Level defaultLevel = Level.INFO;
final TestHandler handler1 = new TestHandler();
final TestHandler handler2 = new TestHandler();
final TestHandler handler3 = new TestHandler();
try (Context ctx1 = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, "a", Level.FINE.toString())).logHandler(handler1).build()) {
Context ctx2 = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, "a.a", Level.FINER.toString())).logHandler(handler2).build();
try (Context ctx3 = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, "a.a.a", Level.FINER.toString())).logHandler(handler3).build()) {
ctx2.close();
ctx1.eval(LoggingLanguageFirst.ID, "");
ctx3.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.singletonMap("a", Level.FINE)));
Assert.assertEquals(expected, handler1.getLog());
expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.singletonMap("a.a.a", Level.FINER)));
Assert.assertEquals(expected, handler3.getLog());
}
}
}
@Test
public void testMultipleContextsNested4() {
final Level defaultLevel = Level.INFO;
final TestHandler handler1 = new TestHandler();
final TestHandler handler2 = new TestHandler();
final TestHandler handler3 = new TestHandler();
try (Context ctx1 = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, "a", Level.FINE.toString())).logHandler(handler1).build()) {
Context ctx2 = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, "a.a", Level.FINER.toString())).logHandler(handler2).build();
try (Context ctx3 = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, "a.a", Level.FINER.toString())).logHandler(handler3).build()) {
ctx2.close();
ctx1.eval(LoggingLanguageFirst.ID, "");
ctx3.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.singletonMap("a", Level.FINE)));
Assert.assertEquals(expected, handler1.getLog());
expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.singletonMap("a.a", Level.FINER)));
Assert.assertEquals(expected, handler3.getLog());
}
}
}
@Test
public void testLogRecordImmutable() {
final TestHandler handler1 = new TestHandler();
try (Context ctx1 = newContextBuilder().logHandler(handler1).build()) {
ctx1.eval(LoggingLanguageFirst.ID, "");
boolean logged = false;
for (LogRecord r : handler1.getRawLog()) {
logged = true;
assertImmutable(r);
}
Assert.assertTrue(logged);
}
}
@Test
public void testParametersPrimitive() {
final Object[] expected = new Object[]{1, 1L, null, 1.1, 1.1d, "test", 't', null, true};
AbstractLoggingLanguage.action = new BiPredicate<LoggingContext, TruffleLogger[]>() {
@Override
public boolean test(final LoggingContext context, final TruffleLogger[] loggers) {
for (TruffleLogger logger : loggers) {
logger.log(Level.WARNING, "Parameters", Arrays.copyOf(expected, expected.length));
}
return false;
}
};
final TestHandler handler1 = new TestHandler();
try (Context ctx1 = newContextBuilder().logHandler(handler1).build()) {
ctx1.eval(LoggingLanguageFirst.ID, "");
boolean logged = false;
for (LogRecord r : handler1.getRawLog()) {
logged = true;
Assert.assertEquals("Parameters", r.getMessage());
Assert.assertArrayEquals(expected, r.getParameters());
}
Assert.assertTrue(logged);
}
}
@Test
public void testParametersObjects() {
AbstractLoggingLanguage.action = new BiPredicate<LoggingContext, TruffleLogger[]>() {
@Override
public boolean test(final LoggingContext context, final TruffleLogger[] loggers) {
for (TruffleLogger logger : loggers) {
logger.log(Level.WARNING, "Parameters", new LoggingLanguageObject("passed"));
}
return false;
}
};
final TestHandler handler1 = new TestHandler();
try (Context ctx1 = newContextBuilder().logHandler(handler1).build()) {
ctx1.eval(LoggingLanguageFirst.ID, "");
boolean logged = false;
for (LogRecord r : handler1.getRawLog()) {
logged = true;
Assert.assertEquals("Parameters", r.getMessage());
Assert.assertArrayEquals(new Object[]{"passed"}, r.getParameters());
}
Assert.assertTrue(logged);
}
}
@Test
public void testInnerContextLogging() {
AbstractLoggingLanguage.action = new BiPredicate<LoggingContext, TruffleLogger[]>() {
@Override
public boolean test(final LoggingContext context, final TruffleLogger[] loggers) {
TruffleContext tc = context.getEnv().newContextBuilder().build();
final Object prev = tc.enter(null);
try {
for (TruffleLogger logger : loggers) {
logger.log(Level.FINEST, "INNER: " + logger.getName());
}
} finally {
tc.leave(null, prev);
tc.close();
}
return true;
}
};
TestHandler handler = new TestHandler();
try (Context ctx = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINEST.toString())).logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
for (String loggerName : AbstractLoggingLanguage.LOGGER_NAMES) {
expected.add(new AbstractMap.SimpleImmutableEntry<>(Level.FINEST, "INNER: " + LoggingLanguageFirst.ID + '.' + loggerName));
}
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.FINEST, Collections.emptyMap()));
Assert.assertEquals(expected, handler.getLog());
}
}
@Test
public void testPolyglotLogHandler() {
Assume.assumeTrue(System.getProperty("polyglot.log.file") == null);
CloseableByteArrayOutputStream err = new CloseableByteArrayOutputStream();
testLogToStream(newContextBuilder().err(err), err, false, true);
}
@Test
public void testGcedContext() {
TestHandler handler = new TestHandler();
Context gcedContext = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINEST.toString())).logHandler(handler).build();
gcedContext.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.FINEST, Collections.emptyMap()));
Assert.assertEquals(expected, handler.getLog());
Reference<Context> gcedContextRef = new WeakReference<>(gcedContext);
gcedContext = null;
GCUtils.assertGc("Cannot free context.", gcedContextRef);
handler = new TestHandler();
Context newContext = newContextBuilder().logHandler(handler).build();
newContext.eval(LoggingLanguageFirst.ID, "");
expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.INFO, Collections.emptyMap()));
Assert.assertEquals(expected, handler.getLog());
}
@Test
public void testGcedContext2() {
TestHandler gcedContextHandler = new TestHandler();
Context gcedContext = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINEST.toString())).logHandler(gcedContextHandler).build();
TestHandler contextHandler = new TestHandler();
Context context = newContextBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINE.toString())).logHandler(contextHandler).build();
gcedContext.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.FINEST, Collections.emptyMap()));
Assert.assertEquals(expected, gcedContextHandler.getLog());
context.eval(LoggingLanguageFirst.ID, "");
expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.FINE, Collections.emptyMap()));
Assert.assertEquals(expected, contextHandler.getLog());
Reference<Context> gcedContextRef = new WeakReference<>(gcedContext);
gcedContext = null;
GCUtils.assertGc("Cannot free context.", gcedContextRef);
contextHandler.clear();
context.eval(LoggingLanguageFirst.ID, "");
expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.FINE, Collections.emptyMap()));
Assert.assertEquals(expected, contextHandler.getLog());
}
@Test
public void testLogToStream() {
CloseableByteArrayOutputStream stream = new CloseableByteArrayOutputStream();
testLogToStream(newContextBuilder().logHandler(stream), stream, true, false);
stream = new CloseableByteArrayOutputStream();
try (Engine engine = newEngineBuilder().logHandler(stream).build()) {
testLogToStream(newContextBuilder().engine(engine), stream, false, false);
stream.clear();
CloseableByteArrayOutputStream innerStream = new CloseableByteArrayOutputStream();
testLogToStream(newContextBuilder().engine(engine).logHandler(innerStream), innerStream, true, false);
Assert.assertFalse(stream.isClosed());
Assert.assertEquals(0, stream.toByteArray().length);
testLogToStream(newContextBuilder().engine(engine), stream, false, false);
}
Assert.assertTrue(stream.isClosed());
}
@Test
public void testDecreaseLogLevelSingleContext() {
Level defaultLevel = Level.INFO;
Map<String, Level> setLevelsMap = new HashMap<>();
setLevelsMap.put("a", Level.FINEST);
setLevelsMap.put("a.a", Level.INFO);
Context.Builder builder = newContextBuilder();
for (Map.Entry<String, Level> levelsMapEntry : setLevelsMap.entrySet()) {
builder.options(createLoggingOptions(LoggingLanguageFirst.ID, levelsMapEntry.getKey(), levelsMapEntry.getValue().toString()));
}
TestHandler handler = new TestHandler();
try (Context ctx = builder.logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, setLevelsMap));
Assert.assertEquals(expected, handler.getLog());
}
}
@Test
public void testDecreaseLogLevelMultipleContexts() {
Level defaultLevel = Level.INFO;
Map<String, Level> setLevelsMap = new HashMap<>();
setLevelsMap.put("a", Level.FINEST);
setLevelsMap.put("a.a", Level.INFO);
Context.Builder builder = newContextBuilder();
for (Map.Entry<String, Level> levelsMapEntry : setLevelsMap.entrySet()) {
builder.options(createLoggingOptions(LoggingLanguageFirst.ID, levelsMapEntry.getKey(), levelsMapEntry.getValue().toString()));
}
TestHandler handler = new TestHandler();
try (Context ctx = builder.logHandler(handler).build()) {
TestHandler handler2 = new TestHandler();
try (Context ctx2 = newContextBuilder().logHandler(handler2).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
ctx2.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, setLevelsMap));
Assert.assertEquals(expected, handler.getLog());
expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.emptyMap()));
Assert.assertEquals(expected, handler2.getLog());
}
}
}
@Test
public void testDecreaseIncreaseLogLevelSingleContext() {
Map<String, Level> setLevelsMap = new HashMap<>();
setLevelsMap.put(null, Level.FINEST);
setLevelsMap.put("a", Level.INFO);
setLevelsMap.put("a.a", Level.FINE);
TestHandler handler = new TestHandler();
Context.Builder builder = newContextBuilder();
for (Map.Entry<String, Level> levelsMapEntry : setLevelsMap.entrySet()) {
builder.options(createLoggingOptions(LoggingLanguageFirst.ID, levelsMapEntry.getKey(), levelsMapEntry.getValue().toString()));
}
try (Context ctx = builder.logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, setLevelsMap.remove(null), setLevelsMap));
Assert.assertEquals(expected, handler.getLog());
}
}
@Test
public void testDecreaseIncreaseLogLevelMultipleContexts() {
Level defaultLevel = Level.INFO;
Map<String, Level> setLevelsMap = new HashMap<>();
setLevelsMap.put(null, Level.FINEST);
setLevelsMap.put("a", Level.INFO);
setLevelsMap.put("a.a", Level.FINE);
Context.Builder builder = newContextBuilder();
for (Map.Entry<String, Level> levelsMapEntry : setLevelsMap.entrySet()) {
builder.options(createLoggingOptions(LoggingLanguageFirst.ID, levelsMapEntry.getKey(), levelsMapEntry.getValue().toString()));
}
TestHandler handler = new TestHandler();
try (Context ctx = builder.logHandler(handler).build()) {
TestHandler handler2 = new TestHandler();
try (Context ctx2 = newContextBuilder().logHandler(handler2).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
ctx2.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, setLevelsMap.remove(null), setLevelsMap));
Assert.assertEquals(expected, handler.getLog());
expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.emptyMap()));
Assert.assertEquals(expected, handler2.getLog());
}
}
}
@Test
public void () {
Map<String, Level> setLevelsMap = new HashMap<>();
setLevelsMap.put(null, Level.FINEST);
setLevelsMap.put("a", Level.OFF);
Context.Builder builder = newContextBuilder();
for (Map.Entry<String, Level> levelsMapEntry : setLevelsMap.entrySet()) {
builder.options(createLoggingOptions(LoggingLanguageFirst.ID, levelsMapEntry.getKey(), levelsMapEntry.getValue().toString()));
}
TestHandler handler = new TestHandler();
try (Context ctx = builder.logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, setLevelsMap.remove(null), setLevelsMap));
Assert.assertEquals(expected, handler.getLog());
}
}
@Test
public void testDisableLoggersMultipleContexts() {
Level defaultLevel = Level.INFO;
Map<String, Level> setLevelsMap = new HashMap<>();
setLevelsMap.put(null, Level.FINEST);
setLevelsMap.put("a", Level.OFF);
Context.Builder builder = newContextBuilder();
for (Map.Entry<String, Level> levelsMapEntry : setLevelsMap.entrySet()) {
builder.options(createLoggingOptions(LoggingLanguageFirst.ID, levelsMapEntry.getKey(), levelsMapEntry.getValue().toString()));
}
TestHandler handler = new TestHandler();
try (Context ctx = builder.logHandler(handler).build()) {
TestHandler handler2 = new TestHandler();
try (Context ctx2 = newContextBuilder().logHandler(handler2).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
ctx2.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, setLevelsMap.remove(null), setLevelsMap));
Assert.assertEquals(expected, handler.getLog());
expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.emptyMap()));
Assert.assertEquals(expected, handler2.getLog());
}
}
}
@Test
public void testNoContextLoggingBasic() {
final Level defaultLevel = Level.INFO;
TestHandler engineHandler = new TestHandler();
try (Engine eng = newEngineBuilder().logHandler(engineHandler).build()) {
TestHandler handler = new TestHandler();
try (Context ctx = newContextBuilder().engine(eng).options(createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINEST.toString())).logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
ctx.eval(LoggingLanguageSecond.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.FINEST, Collections.emptyMap()));
expected.addAll(createExpectedLog(LoggingLanguageSecond.ID, defaultLevel, Collections.emptyMap()));
Assert.assertEquals(expected, handler.getLog());
}
}
Assert.assertTrue(engineHandler.getLog().isEmpty());
engineHandler = new TestHandler();
try (Engine eng = newEngineBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINEST.toString())).logHandler(engineHandler).build()) {
try (Context ctx = newContextBuilder().engine(eng).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
ctx.eval(LoggingLanguageSecond.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.FINEST, Collections.emptyMap()));
expected.addAll(createExpectedLog(LoggingLanguageSecond.ID, defaultLevel, Collections.emptyMap()));
Assert.assertEquals(expected, engineHandler.getLog());
}
}
Assert.assertTrue(engineHandler.getLog().isEmpty());
engineHandler = new TestHandler();
ProxyInstrument delegate = new ProxyInstrument();
delegate.setOnCreate(new InstrumentLogging(false));
ProxyInstrument.setDelegate(delegate);
try (Engine eng = newEngineBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINEST.toString(), ProxyInstrument.ID, null, Level.FINEST.toString())).logHandler(
engineHandler).build()) {
LoggingLanguageFirst.action = new LookupInstrumentAction(true);
try (Context ctx = newContextBuilder().engine(eng).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(ProxyInstrument.ID, Level.FINEST, Collections.emptyMap()));
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.FINEST, Collections.emptyMap()));
Assert.assertEquals(expected, engineHandler.getLog());
}
}
engineHandler = new TestHandler();
delegate = new ProxyInstrument();
delegate.setOnCreate(new InstrumentLogging(true));
ProxyInstrument.setDelegate(delegate);
try (Engine eng = newEngineBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINEST.toString(), ProxyInstrument.ID, null, Level.FINEST.toString())).logHandler(
engineHandler).build()) {
LoggingLanguageFirst.action = new LookupInstrumentAction(true);
try (Context ctx = newContextBuilder().engine(eng).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(ProxyInstrument.ID, Level.FINEST, Collections.emptyMap()));
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.FINEST, Collections.emptyMap()));
Assert.assertEquals(expected, engineHandler.getLog());
}
}
}
@Test
public void testNoContextLoggingDefault() {
final Level defaultLevel = Level.INFO;
TestHandler engineHandler = new TestHandler();
ProxyInstrument delegate = new ProxyInstrument();
delegate.setOnCreate(new InstrumentLogging(false));
ProxyInstrument.setDelegate(delegate);
try (Engine eng = newEngineBuilder().logHandler(engineHandler).build()) {
LoggingLanguageFirst.action = new LookupInstrumentAction(true);
try (Context ctx = newContextBuilder().engine(eng).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(ProxyInstrument.ID, defaultLevel, Collections.emptyMap()));
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.emptyMap()));
Assert.assertEquals(expected, engineHandler.getLog());
}
}
engineHandler = new TestHandler();
delegate = new ProxyInstrument();
delegate.setOnCreate(new InstrumentLogging(true));
ProxyInstrument.setDelegate(delegate);
try (Engine eng = newEngineBuilder().logHandler(engineHandler).build()) {
LoggingLanguageFirst.action = new LookupInstrumentAction(true);
try (Context ctx = newContextBuilder().engine(eng).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(ProxyInstrument.ID, defaultLevel, Collections.emptyMap()));
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, defaultLevel, Collections.emptyMap()));
Assert.assertEquals(expected, engineHandler.getLog());
}
}
}
@Test
public void testNoContextLoggingEngineAndContextHandler() {
TestHandler engineHandler = new TestHandler();
ProxyInstrument delegate = new ProxyInstrument();
delegate.setOnCreate(new InstrumentLogging(false));
ProxyInstrument.setDelegate(delegate);
try (Engine eng = newEngineBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINE.toString(), ProxyInstrument.ID, null, Level.FINE.toString())).logHandler(
engineHandler).build()) {
LoggingLanguageFirst.action = new LookupInstrumentAction(true);
TestHandler contextHandler = new TestHandler();
try (Context ctx = newContextBuilder().engine(eng).options(createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINER.toString())).logHandler(contextHandler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(ProxyInstrument.ID, Level.FINE, Collections.emptyMap()));
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.FINER, Collections.emptyMap()));
Assert.assertEquals(expected, contextHandler.getLog());
Assert.assertTrue(engineHandler.getLog().isEmpty());
}
}
engineHandler = new TestHandler();
delegate = new ProxyInstrument();
delegate.setOnCreate(new InstrumentLogging(true));
ProxyInstrument.setDelegate(delegate);
try (Engine eng = newEngineBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINE.toString(), ProxyInstrument.ID, null, Level.FINE.toString())).logHandler(
engineHandler).build()) {
LoggingLanguageFirst.action = new LookupInstrumentAction(true);
TestHandler contextHandler = new TestHandler();
try (Context ctx = newContextBuilder().engine(eng).options(createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINER.toString())).logHandler(contextHandler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
List<Map.Entry<Level, String>> expectedInEngine = new ArrayList<>();
expectedInEngine.addAll(createExpectedLog(ProxyInstrument.ID, Level.FINE, Collections.emptyMap()));
Assert.assertEquals(expectedInEngine, engineHandler.getLog());
List<Map.Entry<Level, String>> expectedInContext = new ArrayList<>();
expectedInContext.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.FINER, Collections.emptyMap()));
Assert.assertEquals(expectedInContext, contextHandler.getLog());
}
}
}
@Test
public void testNoContextLoggingMultipleEngines() {
TestHandler engine1Handler = new TestHandler();
TestHandler engine2Handler = new TestHandler();
ProxyInstrument delegate = new ProxyInstrument();
delegate.setOnCreate(new InstrumentLogging(true));
ProxyInstrument.setDelegate(delegate);
try (Engine eng1 = newEngineBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINEST.toString(), ProxyInstrument.ID, null, Level.FINEST.toString())).logHandler(
engine1Handler).build()) {
try (Engine eng2 = newEngineBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINE.toString(), ProxyInstrument.ID, null, Level.FINE.toString())).logHandler(
engine2Handler).build()) {
LoggingLanguageFirst.action = new LookupInstrumentAction(true);
try (Context ctx = newContextBuilder().engine(eng1).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
}
LoggingLanguageFirst.action = new LookupInstrumentAction(true);
try (Context ctx = newContextBuilder().engine(eng2).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
}
List<Map.Entry<Level, String>> expected = new ArrayList<>();
expected.addAll(createExpectedLog(ProxyInstrument.ID, Level.FINEST, Collections.emptyMap()));
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.FINEST, Collections.emptyMap()));
Assert.assertEquals(expected, engine1Handler.getLog());
expected.clear();
expected.addAll(createExpectedLog(ProxyInstrument.ID, Level.FINE, Collections.emptyMap()));
expected.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.FINE, Collections.emptyMap()));
Assert.assertEquals(expected, engine2Handler.getLog());
}
}
}
@Test
public void testNoContextLoggingDefaultTruffleLogger() {
TestHandler engineHandler = new TestHandler();
try (Engine eng = newEngineBuilder().options(createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINE.toString(), ProxyInstrument.ID, null, Level.FINE.toString())).logHandler(
engineHandler).build()) {
AtomicReference<TruffleLogger> loggerRef = new AtomicReference<>();
LoggingLanguageFirst.action = new BiPredicate<LoggingContext, TruffleLogger[]>() {
@Override
public boolean test(LoggingContext ctx, TruffleLogger[] loggers) {
Assert.assertTrue(loggers.length > 0);
loggerRef.set(loggers[0]);
return true;
}
};
try (Context ctx = newContextBuilder().engine(eng).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
try {
loggerRef.get().log(Level.INFO, "Should not be logged.");
Assert.assertFalse(assertionsEnabled());
} catch (IllegalStateException e) {
}
List<Map.Entry<Level, String>> expectedInEngine = new ArrayList<>();
expectedInEngine.addAll(createExpectedLog(LoggingLanguageFirst.ID, Level.FINE, Collections.emptyMap()));
Assert.assertEquals(expectedInEngine, engineHandler.getLog());
}
}
}
@Test
public void testEngineLoggerIdentity() {
ProxyInstrument delegate = new ProxyInstrument();
AtomicReferenceArray<TruffleLogger> eng1Loggers = new AtomicReferenceArray<>(2);
delegate.setOnCreate(new Consumer<TruffleInstrument.Env>() {
@Override
public void accept(TruffleInstrument.Env env) {
TruffleLogger logger1 = env.getLogger("a.b.c");
TruffleLogger logger2 = env.getLogger("a.b");
Assert.assertNotSame(logger1, logger2);
Assert.assertSame(logger1, env.getLogger("a.b.c"));
Assert.assertSame(logger2, env.getLogger("a.b"));
Assert.assertNotSame(logger1, TruffleLogger.getLogger(ProxyInstrument.ID, "a.b.c"));
Assert.assertNotSame(logger2, TruffleLogger.getLogger(ProxyInstrument.ID, "a.b"));
eng1Loggers.set(0, logger1);
eng1Loggers.set(1, logger2);
}
});
ProxyInstrument.setDelegate(delegate);
try (Engine eng = Engine.create()) {
LoggingLanguageFirst.action = new LookupInstrumentAction(false);
try (Context ctx = newContextBuilder().engine(eng).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
}
}
delegate = new ProxyInstrument();
delegate.setOnCreate(new Consumer<TruffleInstrument.Env>() {
@Override
public void accept(TruffleInstrument.Env env) {
TruffleLogger logger1 = env.getLogger("a.b.c");
TruffleLogger logger2 = env.getLogger("a.b");
Assert.assertNotSame(logger1, logger2);
Assert.assertNotSame(eng1Loggers.get(0), logger1);
Assert.assertNotSame(eng1Loggers.get(1), logger2);
}
});
ProxyInstrument.setDelegate(delegate);
try (Engine eng = Engine.create()) {
LoggingLanguageFirst.action = new LookupInstrumentAction(false);
try (Context ctx = newContextBuilder().engine(eng).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
}
}
}
@Test
public void testErrorStream() {
Assume.assumeTrue(System.getProperty("polyglot.log.file") == null);
ByteArrayOutputStream errConsumer = new ByteArrayOutputStream();
ProxyInstrument delegate = new ProxyInstrument();
delegate.setOnCreate(new Consumer<TruffleInstrument.Env>() {
@Override
public void accept(TruffleInstrument.Env env) {
env.getInstrumenter().attachErrConsumer(errConsumer);
new InstrumentLogging(false).accept(env);
}
});
ProxyInstrument.setDelegate(delegate);
LoggingLanguageFirst.action = new LookupInstrumentAction(true);
ByteArrayOutputStream err = new ByteArrayOutputStream();
try (Context ctx = newContextBuilder().err(err).options(createLoggingOptions(LoggingLanguageFirst.ID, null, Level.FINE.toString(), ProxyInstrument.ID, null, Level.FINE.toString())).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
}
Assert.assertNotEquals(0, err.toByteArray().length);
Assert.assertEquals(0, errConsumer.toByteArray().length);
}
@Test
public void testInvalidId() {
Context.Builder builder = newContextBuilder();
TestHandler handler = new TestHandler();
LoggingLanguageFirst.action = new BiPredicate<LoggingContext, TruffleLogger[]>() {
@Override
public boolean test(LoggingContext ctx, TruffleLogger[] defaultLoggers) {
try {
TruffleLogger.getLogger("LoggingTest-Invalid-Id");
Assert.fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException iae) {
}
try {
TruffleLogger.getLogger("LoggingTest-Invalid-Id", LoggingTest.class);
Assert.fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException iae) {
}
try {
TruffleLogger.getLogger("LoggingTest-Invalid-Id", "global");
Assert.fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException iae) {
}
return false;
}
};
try (Context ctx = builder.logHandler(handler).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
}
}
@Test
public void testLogFileOption() throws IOException {
File f = File.createTempFile(getClass().getSimpleName(), "log");
String expectedMessage = "expected_message";
AbstractLoggingLanguage.action = createCustomLogging(
new String[]{LoggingLanguageFirst.ID},
new String[]{null},
new String[]{expectedMessage});
try (Context ctx = newContextBuilder().option("log.file", f.getAbsolutePath()).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
}
List<String> lines = Files.readAllLines(f.toPath());
Assert.assertEquals(1, lines.size());
Assert.assertTrue(lines.get(0).contains(expectedMessage));
}
@Test
public void testLogFileOptionMultpileContexts() throws IOException {
File f = File.createTempFile(getClass().getSimpleName(), "log");
String expectedMessageFirstCtx = "expected_message1";
String expectedMessageSecondCtx = "expected_message2";
AbstractLoggingLanguage.action = createCustomLogging(
new String[]{LoggingLanguageFirst.ID},
new String[]{null},
new String[]{expectedMessageFirstCtx});
try (Context ctx = newContextBuilder().option("log.file", f.getAbsolutePath()).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
}
AbstractLoggingLanguage.action = createCustomLogging(
new String[]{LoggingLanguageFirst.ID},
new String[]{null},
new String[]{expectedMessageSecondCtx});
try (Context ctx = newContextBuilder().option("log.file", f.getAbsolutePath()).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
}
List<String> lines = Files.readAllLines(f.toPath());
Assert.assertEquals(2, lines.size());
Assert.assertTrue(lines.get(0).contains(expectedMessageFirstCtx));
Assert.assertTrue(lines.get(1).contains(expectedMessageSecondCtx));
}
@Test
public void testLogFileOptionMultpileContextsNested() throws IOException {
File f = File.createTempFile(getClass().getSimpleName(), "log");
String expectedMessageFirstCtx = "expected_message1";
String expectedMessageSecondCtx = "expected_message2";
AbstractLoggingLanguage.action = createCustomLogging(
new String[]{LoggingLanguageFirst.ID},
new String[]{null},
new String[]{expectedMessageFirstCtx});
try (Context ctx = newContextBuilder().option("log.file", f.getAbsolutePath()).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
AbstractLoggingLanguage.action = createCustomLogging(
new String[]{LoggingLanguageFirst.ID},
new String[]{null},
new String[]{expectedMessageSecondCtx});
try (Context ctx2 = newContextBuilder().option("log.file", f.getAbsolutePath()).build()) {
ctx2.eval(LoggingLanguageFirst.ID, "");
}
}
List<String> lines = Files.readAllLines(f.toPath());
Assert.assertEquals(2, lines.size());
Assert.assertTrue(lines.get(0).contains(expectedMessageFirstCtx));
Assert.assertTrue(lines.get(1).contains(expectedMessageSecondCtx));
}
@Test
public void testLogFileOptionNonWritableFile() throws IOException {
File f = File.createTempFile(getClass().getSimpleName(), "log");
f.setWritable(false);
Assume.assumeFalse("File cannot be writeable.", f.canWrite());
AbstractPolyglotTest.assertFails(() -> {
try (Context ctx = newContextBuilder().option("log.file", f.getAbsolutePath()).build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
}
}, IllegalArgumentException.class, (e) -> {
Assert.assertTrue(e.getMessage().contains("Cannot open log file"));
});
}
@SuppressWarnings("all")
private static boolean assertionsEnabled() {
boolean assertionsEnabled = false;
assert assertionsEnabled = true;
return assertionsEnabled;
}
private static BiPredicate<LoggingContext, TruffleLogger[]> createCustomLogging(String[] loggerIds, String[] loggerNames, String[] messages) {
if (loggerIds.length != loggerNames.length || loggerNames.length != messages.length) {
throw new IllegalArgumentException("loggerIds, loggerNames and messages hsve to have same length.");
}
return new BiPredicate<LoggingContext, TruffleLogger[]>() {
@Override
@CompilerDirectives.TruffleBoundary
public boolean test(final LoggingContext context, final TruffleLogger[] loggers) {
for (int i = 0; i < loggerIds.length; i++) {
String loggerId = loggerIds[i];
String loggerName = loggerNames[i];
String message = messages[i];
if (loggerName == null) {
TruffleLogger.getLogger(loggerId).warning(message);
} else {
TruffleLogger.getLogger(loggerId, loggerName).warning(message);
}
}
return false;
}
};
}
private static void testLogToStream(Context.Builder contextBuilder, CloseableByteArrayOutputStream stream,
boolean expectStreamClosed, boolean expectRedirectMessage) {
AbstractLoggingLanguage.action = createCustomLogging(
new String[]{LoggingLanguageFirst.ID, LoggingLanguageFirst.ID},
new String[]{null, "package.class"},
new String[]{LoggingLanguageFirst.ID, LoggingLanguageFirst.ID + "::package.class"});
try (Context ctx = contextBuilder.build()) {
ctx.eval(LoggingLanguageFirst.ID, "");
}
Assert.assertEquals(expectStreamClosed, stream.isClosed());
String output = new String(stream.toByteArray());
if (expectRedirectMessage) {
String redirectMessage = getRedirectMessage();
Assert.assertTrue(output.startsWith(redirectMessage));
output = output.substring(redirectMessage.length());
}
final Pattern p = Pattern.compile("\\[(.*)\\]\\sWARNING:\\s(.*)");
for (String line : output.split("\n")) {
final Matcher m = p.matcher(line.trim());
Assert.assertTrue(m.matches());
final String loggerName = m.group(1);
final String message = m.group(2);
Assert.assertEquals(message, loggerName);
}
}
private static String getRedirectMessage() {
try {
Class<?> clz = Class.forName("com.oracle.truffle.polyglot.PolyglotLoggers$RedirectNotificationOutputStream");
Field fld = clz.getDeclaredField("REDIRECT_FORMAT");
fld.setAccessible(true);
String format = (String) fld.get(null);
return String.format(format);
} catch (ReflectiveOperationException e) {
throw new AssertionError("Cannot read redirect log message.", e);
}
}
private static void assertImmutable(final LogRecord r) {
try {
r.setLevel(Level.FINEST);
Assert.fail("Should not reach here.");
} catch (UnsupportedOperationException e) {
}
try {
r.setLoggerName("test");
Assert.fail("Should not reach here.");
} catch (UnsupportedOperationException e) {
}
try {
r.setMessage("test");
Assert.fail("Should not reach here.");
} catch (UnsupportedOperationException e) {
}
try {
setMillis(r, 10);
Assert.fail("Should not reach here.");
} catch (UnsupportedOperationException e) {
}
try {
r.setParameters(new Object[0]);
Assert.fail("Should not reach here.");
} catch (UnsupportedOperationException e) {
}
try {
r.setResourceBundle(null);
Assert.fail("Should not reach here.");
} catch (UnsupportedOperationException e) {
}
try {
r.setResourceBundleName(null);
Assert.fail("Should not reach here.");
} catch (UnsupportedOperationException e) {
}
try {
r.setSequenceNumber(10);
Assert.fail("Should not reach here.");
} catch (UnsupportedOperationException e) {
}
try {
r.setSourceClassName("Test");
Assert.fail("Should not reach here.");
} catch (UnsupportedOperationException e) {
}
try {
r.setSourceMethodName("test");
Assert.fail("Should not reach here.");
} catch (UnsupportedOperationException e) {
}
try {
r.setThreadID(10);
Assert.fail("Should not reach here.");
} catch (UnsupportedOperationException e) {
}
try {
r.setThrown(new NullPointerException());
Assert.fail("Should not reach here.");
} catch (UnsupportedOperationException e) {
}
}
@SuppressWarnings("deprecation")
private static void setMillis(final LogRecord r, long value) {
r.setMillis(value);
}
private static Map<String, String> createLoggingOptions(final String... kvs) {
if ((kvs.length % 3) != 0) {
throw new IllegalArgumentException("Lang, Key, Val length has to be divisible by 3.");
}
final Map<String, String> options = new HashMap<>();
for (int i = 0; i < kvs.length; i += 3) {
final String key;
if (kvs[i] == null) {
assert kvs[i + 1] == null;
key = "log.level";
} else if (kvs[i + 1] == null) {
key = String.format("log.%s.level", kvs[i]);
} else {
key = String.format("log.%s.%s.level", kvs[i], kvs[i + 1]);
}
options.put(key, kvs[i + 2]);
}
return options;
}
private static List<Map.Entry<Level, String>> createExpectedLog(final String languageId, final Level defaultLevel, final Map<String, Level> levels) {
final LoggerNode root = levels.isEmpty() ? null : createLevelsTree(levels);
final List<Map.Entry<Level, String>> res = new ArrayList<>();
for (Level level : AbstractLoggingLanguage.LOGGER_LEVELS) {
for (String loggerName : AbstractLoggingLanguage.LOGGER_NAMES) {
final Level loggerLevel = root == null ? defaultLevel : root.computeLevel(loggerName, defaultLevel);
if (loggerLevel.intValue() <= level.intValue()) {
res.add(new AbstractMap.SimpleImmutableEntry<>(
level,
String.format("%s.%s", languageId, loggerName)));
}
}
}
return res;
}
private static LoggerNode createLevelsTree(Map<String, Level> levels) {
final LoggerNode root = new LoggerNode();
for (Map.Entry<String, Level> level : levels.entrySet()) {
final String loggerName = level.getKey();
final Level loggerLevel = level.getValue();
final LoggerNode node = root.findChild(loggerName);
node.level = loggerLevel;
}
return root;
}
private static Context.Builder newContextBuilder() {
return Context.newBuilder().options(createLoggingOptions("engine", null, Level.OFF.toString()));
}
private static Engine.Builder newEngineBuilder() {
return Engine.newBuilder().options(createLoggingOptions("engine", null, Level.OFF.toString()));
}
public static final class LoggingContext {
private final TruffleLanguage.Env env;
LoggingContext(final TruffleLanguage.Env env) {
this.env = env;
}
TruffleLanguage.Env getEnv() {
return env;
}
}
private static final class LoggerNode {
private final Map<String, LoggerNode> children;
private Level level;
LoggerNode() {
this.children = new HashMap<>();
}
LoggerNode findChild(String loggerName) {
if (loggerName.isEmpty()) {
return this;
}
int index = loggerName.indexOf('.');
String currentNameCompoment;
String nameRest;
if (index > 0) {
currentNameCompoment = loggerName.substring(0, index);
nameRest = loggerName.substring(index + 1);
} else {
currentNameCompoment = loggerName;
nameRest = "";
}
LoggerNode child = children.get(currentNameCompoment);
if (child == null) {
child = new LoggerNode();
children.put(currentNameCompoment, child);
}
return child.findChild(nameRest);
}
Level computeLevel(final String loggerName, final Level bestSoFar) {
Level res = bestSoFar;
if (this.level != null) {
res = level;
}
if (loggerName.isEmpty()) {
return res;
}
int index = loggerName.indexOf('.');
String currentNameCompoment;
String nameRest;
if (index > 0) {
currentNameCompoment = loggerName.substring(0, index);
nameRest = loggerName.substring(index + 1);
} else {
currentNameCompoment = loggerName;
nameRest = "";
}
LoggerNode child = children.get(currentNameCompoment);
if (child == null) {
return res;
}
return child.computeLevel(nameRest, res);
}
}
@ExportLibrary(InteropLibrary.class)
@SuppressWarnings("static-method")
static final class LoggingLanguageObject implements TruffleObject {
final String stringValue;
LoggingLanguageObject(final String stringValue) {
this.stringValue = stringValue;
}
@ExportMessage
Object toDisplayString(@SuppressWarnings("unused") boolean allowSideEffects) {
return stringValue;
}
@ExportMessage
boolean hasLanguage() {
return true;
}
@ExportMessage
Class<? extends TruffleLanguage<?>> getLanguage() {
return LoggingLanguageFirst.class;
}
}
public abstract static class AbstractLoggingLanguage extends TruffleLanguage<LoggingContext> {
static final String[] LOGGER_NAMES = {"a", "a.a", "a.b", "a.a.a", "b", "b.a", "b.a.a.a"};
static final Level[] LOGGER_LEVELS = {Level.FINEST, Level.FINER, Level.FINE, Level.INFO, Level.SEVERE, Level.WARNING};
static BiPredicate<LoggingContext, TruffleLogger[]> action;
private final TruffleLogger[] allLoggers;
AbstractLoggingLanguage(final String id) {
final ArrayList<TruffleLogger> loggers = new ArrayList<>(LOGGER_NAMES.length);
for (String loggerName : LOGGER_NAMES) {
loggers.add(TruffleLogger.getLogger(id, loggerName));
}
allLoggers = loggers.toArray(new TruffleLogger[0]);
}
@Override
protected LoggingContext createContext(Env env) {
return new LoggingContext(env);
}
@Override
protected CallTarget parse(ParsingRequest request) throws Exception {
Class<? extends AbstractLoggingLanguage> language = getClass();
final RootNode root = new RootNode(this) {
@Override
public Object execute(VirtualFrame frame) {
boolean doDefaultLogging = true;
if (action != null) {
doDefaultLogging = action.test(lookupContextReference(language).get(), allLoggers);
}
if (doDefaultLogging) {
doLog();
}
return lookupContextReference(language).get().getEnv().asGuestValue(null);
}
};
return Truffle.getRuntime().createCallTarget(root);
}
private void doLog() {
for (Level level : LOGGER_LEVELS) {
for (TruffleLogger logger : allLoggers) {
logger.log(level, logger.getName());
}
}
}
}
@TruffleLanguage.Registration(id = LoggingLanguageFirst.ID, name = LoggingLanguageFirst.ID, version = "1.0")
public static final class LoggingLanguageFirst extends AbstractLoggingLanguage {
static final String ID = "log1";
public LoggingLanguageFirst() {
super(ID);
}
}
@TruffleLanguage.Registration(id = LoggingLanguageSecond.ID, name = LoggingLanguageSecond.ID, version = "1.0")
public static final class LoggingLanguageSecond extends AbstractLoggingLanguage {
static final String ID = "log2";
public LoggingLanguageSecond() {
super(ID);
}
}
private static final class TestHandler extends Handler {
private final List<LogRecord> logRecords;
TestHandler() {
this.logRecords = new ArrayList<>();
}
@Override
public void publish(final LogRecord record) {
final String message = record.getMessage();
Assert.assertNotNull(message);
final Level level = record.getLevel();
Assert.assertNotNull(level);
logRecords.add(record);
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
clear();
}
List<Map.Entry<Level, String>> getLog() {
final List<Map.Entry<Level, String>> res = new ArrayList<>();
for (LogRecord r : logRecords) {
res.add(new AbstractMap.SimpleImmutableEntry<>(r.getLevel(), r.getMessage()));
}
return res;
}
List<LogRecord> getRawLog() {
return logRecords;
}
void clear() {
logRecords.clear();
}
}
private static final class CloseableByteArrayOutputStream extends ByteArrayOutputStream {
private boolean closed;
@Override
public void close() throws IOException {
closed = true;
super.close();
}
boolean isClosed() {
return closed;
}
void clear() {
this.count = 0;
}
}
private static final class InstrumentLogging implements Consumer<TruffleInstrument.Env> {
static final String[] LOGGER_NAMES = {"a", "a.a", "a.b", "a.a.a", "b", "b.a", "b.a.a.a"};
static final Level[] LOGGER_LEVELS = {Level.FINEST, Level.FINER, Level.FINE, Level.INFO, Level.SEVERE, Level.WARNING};
private final boolean detached;
InstrumentLogging(boolean detached) {
this.detached = detached;
}
@Override
public void accept(TruffleInstrument.Env env) {
if (detached) {
Thread t = new Thread(() -> {
doLogging(env);
});
t.start();
try {
t.join(10_000);
} catch (InterruptedException ie) {
throw new RuntimeException(ie);
}
} else {
doLogging(env);
}
}
private static void doLogging(TruffleInstrument.Env env) {
for (Level level : LOGGER_LEVELS) {
for (String loggerName : LOGGER_NAMES) {
TruffleLogger logger = env.getLogger(loggerName);
logger.log(level, logger.getName());
}
}
}
}
private static final class LookupInstrumentAction implements BiPredicate<LoggingContext, TruffleLogger[]> {
private final boolean performDefaultLogging;
LookupInstrumentAction(boolean performDefaultLogging) {
this.performDefaultLogging = performDefaultLogging;
}
@Override
public boolean test(LoggingContext ctx, TruffleLogger[] loggers) {
TruffleLanguage.Env env = ctx.getEnv();
InstrumentInfo instrumentInfo = env.getInstruments().get(ProxyInstrument.ID);
env.lookup(instrumentInfo, ProxyInstrument.Initialize.class);
return performDefaultLogging;
}
}
}