3
0
mirror of https://github.com/Qortal/AT.git synced 2025-02-01 03:32:13 +00:00

Improved logging support

We now use a logger factory, which means loggers can be created
per class.

This allows use of 'wrapped' Apache log4j2 loggers with the added
benefit of more accurate class/line-number reporting.
This commit is contained in:
catbref 2020-04-14 09:22:21 +01:00
parent ee2a81430c
commit 466ffa4d4d
9 changed files with 61 additions and 34 deletions

View File

@ -0,0 +1,11 @@
package org.ciyam.at;
public interface AtLogger {
void error(final String message);
void debug(final String message);
void echo(final String message);
}

View File

@ -0,0 +1,7 @@
package org.ciyam.at;
public interface AtLoggerFactory {
AtLogger create(final Class<?> loggerName);
}

View File

@ -1,11 +0,0 @@
package org.ciyam.at;
public interface LoggerInterface {
public void error(String message);
public void debug(String message);
public void echo(String message);
}

View File

@ -126,7 +126,8 @@ public class MachineState {
private boolean isFirstOpCodeAfterSleeping; private boolean isFirstOpCodeAfterSleeping;
private API api; private API api;
private LoggerInterface logger; private AtLoggerFactory loggerFactory;
private AtLogger logger;
// NOTE: These are package-scope to allow easy access/operations in Opcode/FunctionCode. // NOTE: These are package-scope to allow easy access/operations in Opcode/FunctionCode.
/* package */ ByteBuffer codeByteBuffer; /* package */ ByteBuffer codeByteBuffer;
@ -137,7 +138,7 @@ public class MachineState {
// Constructors // Constructors
/** For internal use when recreating a machine state */ /** For internal use when recreating a machine state */
private MachineState(API api, LoggerInterface logger, byte[] headerBytes) { private MachineState(byte[] headerBytes, API api, AtLoggerFactory loggerFactory) {
if (headerBytes.length != HEADER_LENGTH) if (headerBytes.length != HEADER_LENGTH)
throw new IllegalArgumentException("headerBytes length " + headerBytes.length + " incorrect, expected " + HEADER_LENGTH); throw new IllegalArgumentException("headerBytes length " + headerBytes.length + " incorrect, expected " + HEADER_LENGTH);
@ -190,12 +191,13 @@ public class MachineState {
this.currentBalance = 0; this.currentBalance = 0;
this.previousBalance = 0; this.previousBalance = 0;
this.steps = 0; this.steps = 0;
this.logger = logger; this.loggerFactory = loggerFactory;
this.logger = loggerFactory.create(MachineState.class);
} }
/** For creating a new machine state */ /** For creating a new machine state */
public MachineState(API api, byte[] creationBytes) { public MachineState(API api, AtLoggerFactory loggerFactory, byte[] creationBytes) {
this(api, null, Arrays.copyOfRange(creationBytes, 0, HEADER_LENGTH)); this(Arrays.copyOfRange(creationBytes, 0, HEADER_LENGTH), api, loggerFactory);
int expectedLength = HEADER_LENGTH + this.numCodePages * this.constants.CODE_PAGE_SIZE + this.numDataPages * this.constants.DATA_PAGE_SIZE; int expectedLength = HEADER_LENGTH + this.numCodePages * this.constants.CODE_PAGE_SIZE + this.numDataPages * this.constants.DATA_PAGE_SIZE;
if (creationBytes.length != expectedLength) if (creationBytes.length != expectedLength)
@ -210,8 +212,8 @@ public class MachineState {
} }
/** For creating a new machine state - used in tests */ /** For creating a new machine state - used in tests */
public MachineState(API api, LoggerInterface logger, byte[] headerBytes, byte[] codeBytes, byte[] dataBytes) { public MachineState(API api, AtLoggerFactory loggerFactory, byte[] headerBytes, byte[] codeBytes, byte[] dataBytes) {
this(api, logger, headerBytes); this(headerBytes, api, loggerFactory);
if (codeBytes.length > this.numCodePages * this.constants.CODE_PAGE_SIZE) if (codeBytes.length > this.numCodePages * this.constants.CODE_PAGE_SIZE)
throw new IllegalArgumentException("Number of code pages too small to hold code bytes"); throw new IllegalArgumentException("Number of code pages too small to hold code bytes");
@ -342,7 +344,11 @@ public class MachineState {
return this.api; return this.api;
} }
public LoggerInterface getLogger() { public AtLoggerFactory getLoggerFactory() {
return this.loggerFactory;
}
/* package */ AtLogger getLogger() {
return this.logger; return this.logger;
} }
@ -516,13 +522,13 @@ public class MachineState {
} }
/** For restoring a previously serialized machine state */ /** For restoring a previously serialized machine state */
public static MachineState fromBytes(API api, LoggerInterface logger, byte[] bytes, byte[] codeBytes) { public static MachineState fromBytes(API api, AtLoggerFactory loggerFactory, byte[] bytes, byte[] codeBytes) {
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
byte[] headerBytes = new byte[HEADER_LENGTH]; byte[] headerBytes = new byte[HEADER_LENGTH];
byteBuffer.get(headerBytes); byteBuffer.get(headerBytes);
MachineState state = new MachineState(api, logger, headerBytes); MachineState state = new MachineState(headerBytes, api, loggerFactory);
if (codeBytes.length != state.codeByteBuffer.capacity()) if (codeBytes.length != state.codeByteBuffer.capacity())
throw new IllegalStateException("Passed codeBytes does not match length in header"); throw new IllegalStateException("Passed codeBytes does not match length in header");

View File

@ -62,7 +62,7 @@ public class MiscTests extends ExecutableTest {
byte[] codeBytes = codeByteBuffer.array(); byte[] codeBytes = codeByteBuffer.array();
byte[] dataBytes = new byte[0]; byte[] dataBytes = new byte[0];
state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes); state = new MachineState(api, loggerFactory, headerBytes, codeBytes, dataBytes);
assertTrue(state.isFrozen()); assertTrue(state.isFrozen());
assertEquals((Long) (minActivationAmount - 1L), state.getFrozenBalance()); assertEquals((Long) (minActivationAmount - 1L), state.getFrozenBalance());

View File

@ -102,12 +102,12 @@ public class SerializationTests extends ExecutableTest {
byte[] codeBytes = codeByteBuffer.array(); byte[] codeBytes = codeByteBuffer.array();
byte[] dataBytes = dataByteBuffer.array(); byte[] dataBytes = dataByteBuffer.array();
state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes); state = new MachineState(api, loggerFactory, headerBytes, codeBytes, dataBytes);
packedState = state.toBytes(); packedState = state.toBytes();
byte[] creationBytes = MachineState.toCreationBytes(TestUtils.VERSION, codeBytes, dataBytes, TestUtils.NUM_CALL_STACK_PAGES, TestUtils.NUM_USER_STACK_PAGES, TestUtils.MIN_ACTIVATION_AMOUNT); byte[] creationBytes = MachineState.toCreationBytes(TestUtils.VERSION, codeBytes, dataBytes, TestUtils.NUM_CALL_STACK_PAGES, TestUtils.NUM_USER_STACK_PAGES, TestUtils.MIN_ACTIVATION_AMOUNT);
MachineState restoredState = new MachineState(api, creationBytes); MachineState restoredState = new MachineState(api, loggerFactory, creationBytes);
byte[] packedRestoredSate = restoredState.toBytes(); byte[] packedRestoredSate = restoredState.toBytes();
assertTrue(Arrays.equals(packedState, packedRestoredSate)); assertTrue(Arrays.equals(packedState, packedRestoredSate));
@ -118,14 +118,14 @@ public class SerializationTests extends ExecutableTest {
byte[] codeBytes = codeByteBuffer.array(); byte[] codeBytes = codeByteBuffer.array();
byte[] dataBytes = new byte[0]; byte[] dataBytes = new byte[0];
state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes); state = new MachineState(api, loggerFactory, headerBytes, codeBytes, dataBytes);
return executeAndCheck(state); return executeAndCheck(state);
} }
private byte[] continueSimulation(byte[] savedState) { private byte[] continueSimulation(byte[] savedState) {
byte[] codeBytes = codeByteBuffer.array(); byte[] codeBytes = codeByteBuffer.array();
state = MachineState.fromBytes(api, logger, savedState, codeBytes); state = MachineState.fromBytes(api, loggerFactory, savedState, codeBytes);
// Pretend we're on next block // Pretend we're on next block
api.bumpCurrentBlockHeight(); api.bumpCurrentBlockHeight();
@ -141,7 +141,7 @@ public class SerializationTests extends ExecutableTest {
byte[] codeBytes = state.getCodeBytes(); byte[] codeBytes = state.getCodeBytes();
// Rebuild new MachineState using fetched state & bytes // Rebuild new MachineState using fetched state & bytes
MachineState restoredState = MachineState.fromBytes(api, logger, stateBytes, codeBytes); MachineState restoredState = MachineState.fromBytes(api, loggerFactory, stateBytes, codeBytes);
// Extract rebuilt state and code bytes // Extract rebuilt state and code bytes
byte[] restoredStateBytes = restoredState.toBytes(); byte[] restoredStateBytes = restoredState.toBytes();
byte[] restoredCodeBytes = state.getCodeBytes(); byte[] restoredCodeBytes = state.getCodeBytes();

View File

@ -14,7 +14,7 @@ public abstract class ExecutableTest {
private static final int DATA_OFFSET = MachineState.HEADER_LENGTH; // code bytes are not present private static final int DATA_OFFSET = MachineState.HEADER_LENGTH; // code bytes are not present
private static final int CALL_STACK_OFFSET = DATA_OFFSET + TestUtils.NUM_DATA_PAGES * MachineState.VALUE_SIZE; private static final int CALL_STACK_OFFSET = DATA_OFFSET + TestUtils.NUM_DATA_PAGES * MachineState.VALUE_SIZE;
public TestLogger logger; public TestLoggerFactory loggerFactory;
public TestAPI api; public TestAPI api;
public MachineState state; public MachineState state;
public ByteBuffer codeByteBuffer; public ByteBuffer codeByteBuffer;
@ -32,7 +32,7 @@ public abstract class ExecutableTest {
@Before @Before
public void beforeTest() { public void beforeTest() {
logger = new TestLogger(); loggerFactory = new TestLoggerFactory();
api = new TestAPI(); api = new TestAPI();
codeByteBuffer = ByteBuffer.allocate(TestUtils.NUM_CODE_PAGES * MachineState.OPCODE_SIZE); codeByteBuffer = ByteBuffer.allocate(TestUtils.NUM_CODE_PAGES * MachineState.OPCODE_SIZE);
dataByteBuffer = ByteBuffer.allocate(TestUtils.NUM_DATA_PAGES * MachineState.VALUE_SIZE); dataByteBuffer = ByteBuffer.allocate(TestUtils.NUM_DATA_PAGES * MachineState.VALUE_SIZE);
@ -47,7 +47,7 @@ public abstract class ExecutableTest {
codeByteBuffer = null; codeByteBuffer = null;
dataByteBuffer = null; dataByteBuffer = null;
api = null; api = null;
logger = null; loggerFactory = null;
} }
protected void execute(boolean onceOnly) { protected void execute(boolean onceOnly) {
@ -58,12 +58,12 @@ public abstract class ExecutableTest {
if (packedState == null) { if (packedState == null) {
// First time // First time
System.out.println("First execution - deploying..."); System.out.println("First execution - deploying...");
state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes); state = new MachineState(api, loggerFactory, headerBytes, codeBytes, dataBytes);
packedState = state.toBytes(); packedState = state.toBytes();
} }
do { do {
state = MachineState.fromBytes(api, logger, packedState, codeBytes); state = MachineState.fromBytes(api, loggerFactory, packedState, codeBytes);
System.out.println("Starting execution round!"); System.out.println("Starting execution round!");
System.out.println("Current block height: " + api.getCurrentBlockHeight()); System.out.println("Current block height: " + api.getCurrentBlockHeight());

View File

@ -1,8 +1,9 @@
package org.ciyam.at.test; package org.ciyam.at.test;
import org.ciyam.at.LoggerInterface; import org.ciyam.at.AtLogger;
public class TestLogger implements AtLogger {
public class TestLogger implements LoggerInterface {
@Override @Override
public void error(String message) { public void error(String message) {
System.err.println("ERROR: " + message); System.err.println("ERROR: " + message);

View File

@ -0,0 +1,13 @@
package org.ciyam.at.test;
import org.ciyam.at.AtLogger;
import org.ciyam.at.AtLoggerFactory;
public class TestLoggerFactory implements AtLoggerFactory {
@Override
public AtLogger create(Class<?> loggerName) {
return new TestLogger();
}
}