Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor/logging #495

Merged
merged 31 commits into from Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d0d6f90
Add example
lanarimarco Apr 8, 2024
fff03e9
Convert log channels to enum
dom-apuliasoft Apr 9, 2024
7cd9634
Refactor log system
dom-apuliasoft Apr 11, 2024
ec74433
Add performance recap
dom-apuliasoft Apr 11, 2024
1571545
Fix ForStmt serialization issue
dom-apuliasoft Apr 11, 2024
bdfc990
Fix missing logs in symbol_table_storaging.kt
dom-apuliasoft Apr 12, 2024
c4ce003
Remove code duplication in CallPStmt
dom-apuliasoft Apr 12, 2024
88e4c16
Merge branch 'develop' into refactor/logging
dom-apuliasoft Apr 12, 2024
91b6cad
Fix unit test regression. The log format is a little changed.
lanarimarco Apr 12, 2024
70db067
Fix executePROCEDURE_S
dom-apuliasoft Apr 12, 2024
242fc83
Move LoggingContext to MainExecutionContext
dom-apuliasoft Apr 15, 2024
c5ddd6f
Fix RunnerTest
dom-apuliasoft Apr 15, 2024
5cc7c4c
Fix EXPR log entries
dom-apuliasoft Apr 15, 2024
e8d6a06
Merge remote-tracking branch 'origin/refactor/logging' into refactor/…
dom-apuliasoft Apr 15, 2024
3fdb7f1
Fix LoggingTest issues
dom-apuliasoft Apr 15, 2024
e9df6c1
Improve logging time measurement
dom-apuliasoft Apr 15, 2024
aba04db
Extend STMT logging
dom-apuliasoft Apr 15, 2024
028132b
Cleanup logging
dom-apuliasoft Apr 15, 2024
3f6c783
Add ANALYTICS logging channel
dom-apuliasoft Apr 17, 2024
2e741dd
Adjust analytic logs to make them compliant with other logs
dom-apuliasoft Apr 17, 2024
a180718
Remove experimental time functionalities
dom-apuliasoft Apr 18, 2024
2b95f7a
Merge branch 'develop' into refactor/logging
dom-apuliasoft Apr 18, 2024
27e3f94
Update docs and cleanup
dom-apuliasoft Apr 22, 2024
4d17af8
Merge branch 'develop' into refactor/logging
dom-apuliasoft Apr 22, 2024
7bddeac
Add analytics channel to docs
dom-apuliasoft Apr 22, 2024
c5879d3
Add section in docs describing how the system works
dom-apuliasoft Apr 23, 2024
9208db9
Add kotlin docs
dom-apuliasoft Apr 23, 2024
6fb5b7b
Add missing time information to PARS channel
dom-apuliasoft Apr 23, 2024
7634de1
Remove inferable data from analytics
dom-apuliasoft Apr 23, 2024
c9cddb2
Fix RESL channel not logging strategies
dom-apuliasoft Apr 23, 2024
7c001bc
Merge branch 'develop' into refactor/logging
dom-apuliasoft Apr 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
118 changes: 56 additions & 62 deletions docs/logging.md
Expand Up @@ -7,7 +7,7 @@ The logging permits to monitor the interpreter behaviour at the runtime. The log

The output consists of a set of data records, with a fixed header segment with common data and a variable data segment specific for each channel.

The values of the record are separated by a character specified in the configuration file. This permits to easily process the resulting logs as CSV files or using comman line tools such as cut.
The values of the record are separated by a character specified in the configuration file. This permits to easily process the resulting logs as CSV files or using comman line tools such as cut.

The available channels are:

Expand All @@ -19,6 +19,7 @@ The available channels are:
* **Parsing:** measures parsing phase time.
* **Resolution:** provides information about the process to identify the routines or programs to invoke.
* **Error:** provides information about error event occurred during the whole cycle of program interpretation.
* **Analytics** provides information about higher level data collected during the whole cycle of program interpretation.

## Sample

Expand Down Expand Up @@ -96,14 +97,14 @@ and **logger.file.name**.

## Passing the configuration without a file

If you don't want to pass the configuration to the logging system using a file, you could use the ```consoleLoggingConfiguration``` or the ```fileLoggingConfiguration``` fun.
If you don't want to pass the configuration to the logging system using a file, you could use the ```consoleLoggingConfiguration``` or the ```fileLoggingConfiguration``` fun combined with the `LogChannel` enum.
For example:
```
val si = JavaSystemInterface(consoleLoggingConfiguration(EXPRESSION_LOGGER, PERFORMANCE_LOGGER))
val si = JavaSystemInterface(consoleLoggingConfiguration(LogChannel.EXPRESSION, LogChannel.PERFORMANCE))
```
or
```
val si = JavaSystemInterface(fileLoggingConfiguration(File("/home/pippo", "example.log"), EXPRESSION_LOGGER, PERFORMANCE_LOGGER))
val si = JavaSystemInterface(fileLoggingConfiguration(File("/home/pippo", "example.log"), LogChannel.EXPRESSION, LogChannel.PERFORMANCE))
```

## Log file format
Expand All @@ -113,8 +114,8 @@ variable data segment which depends on the channel.


```
<TIMESTAMP><S><PROGRAM><S><LINE><S><CHANNEL><S><CHANNEL SPECIFIC>
+------------------ header -------------------+----- data ------+
<TIMESTAMP><S><CHANNEL><S><PROGRAM><S><LINE><S><ACTION><S><CHANNEL SPECIFIC>
+------------------ header ------------------------------+----- data ------+
```

The header contains the following data:
Expand All @@ -127,53 +128,44 @@ The header contains the following data:
The **S** represent the separator character specified in the configuration.

## Data Channel DATA
The monitors the accesses to the variables during the program execution.
The data channel monitors the accesses to the variables during the program execution.

```
14:25:29.471 TEST_06 DATA NBR = 0 10
+-----------+-------------+--+---+---- data -----+- result -+
14:53:47.263 DATA MUTE10_10 16 ASSIGN $V = 1 was: 0
+------------ header ---------------+-------+--- data ----+
```
The log record collects the initial value zero in this case and the new value
assigned by a statement. In the example above the value of variable NBR has an
initial value of 0 (zero) and assume the value of 10.
assigned by a statement. In the example above the value of variable V has an
initial value of 0 (zero) and assume the value of 1.


## Statement Channel STMT
The statement channel captures information about the statement executed.

```
11:30:38.893 TEST_06 45 STMT EVAL WORDINC = J - I 5
+-----------+-------------+--+---+---- statement -----+- result -+
15:04:57.031 STMT MUTE10_10 47 EXEC EVAL $TIMMS = $TIMMS / 1000
+-------------- header ---------------+------+-------- statement ---------+
```

In the example above the record contains the statement executed `EVAL WORDINC = J - I`
and the result of the expression, in this case **5**.
In the example above the record contains the statement executed `EVAL $TIMMS = $TIMMS / 1000`.

When statement evaluate a comparison operator the result represent the logical
value of the expression.

```
11:30:38.893 TEST_06 45 STMT SELECT WHEN NBR = 0 (false)
11:30:38.893 TEST_06 47 STMT SELECT WHEN NBR = 1 (true)
+-----------+-------------+--+---+---- statement -----+- result -+
```
Some statements might also emit proper and more specific information in the data section of the log entry.

The statement channel also tracks the start and the end of a
program or subroutine.

```
11:30:38.893 TEST_06 45 STMT SELECT SUBROUTINE START FIB

11:30:38.893 TEST_06 55 STMT SUBROUTINE END FIB
+-----------+-------------+--+---+--------- statement ---------+
15:16:28.859 STMT MUTE10_10 START INTERPRETATION
15:04:57.041 STMT MUTE10_10 END INTERPRETATION
+-------------- header ---------------+--------+---- stmt ----+
```

## Expression Channel EXPR
The expression channel collect all the expressions encountered during the program execution.

```
14:14:30.330 TEST_06 28 EXPR A + B 3
+-----------+-------------+--+---+-- expression --+- result -+
15:59:20.546 EXPR MULANGTC10 12 EVAL C10_P2 = 1 true
+-------------------- header ----------------+---- expr ----+----+
```


Expand All @@ -184,48 +176,42 @@ when the loop exits. The end loop record include the number of cycles
actually executed.

```
14:14:30.330 TEST_06 28 LOOP FOR J = 1 TO 4
14:14:30.571 TEST_06 35 LOOP ENDFOR J 4
+-----------+-------------+--+---+----- loop ------+- result -+
10:01:16.243 LOOP MUTE10_10 39 START DO
10:01:16.631 LOOP MUTE10_10 39 END DO 100000
+--------------- header -------------+--- loop ---+------+
```

The example below shows a DOW loop execution.

```
14:14:30.330 TEST_06 13 LOOP DOW LOOP START COUNT < 100
14:14:30.556 TEST_06 22 STMT LEAVE
14:14:30.571 TEST_06 33 LOOP DOW LOOP END 45
+-----------+-------------+--+---+---------- loop -----------+- result -+
```
Please note that statements like LEAVE may affect the number of cycles
actually executed.

## Performance Channel PERF
The performance channel measures the execution time of loops, programs and
subroutines.
The performance channel measures the execution time of statements.
The log records are generated at the end of statements block, measuring the
time in milliseconds.
time in microseconds.

```
15:09:46.910 TEST_06 79 PERF ENDFOR I 8 ms
15:09:46.910 TEST_06 80 PERF SUBROUTINE END PRINT 9 ms
15:09:46.910 TEST_06 PERF END TEST_06 160 ms
+-----------+-------------+--+---+---------- data -----------+- result -+
15:51:55.838 PERF MULANGTC10 7 PLIST elapsed 8.875us
15:51:55.840 PERF MULANGTC10 13 EVAL elapsed 566us
15:51:55.840 PERF MULANGTC10 14 EVAL elapsed 35.084us
+---------------- header -----------------+--------+----- perf ----+
```

## Parsing Channel PARS
The parsing channel measures the parsing time needed to build AST.
The log records are generated at the end of each phase related to parsing.

```
12:36:33 MUTEXX 182 PARS PREPROP END MUTEXX 165 ms
12:36:33 MUTEXX 182 PARS RPGLOAD END MUTEXX 127 ms
12:36:33 MUTEXX PARS LEXER END MUTEXX 236 ms
12:36:34 MUTEXX PARS PARSER END MUTEXX 674 ms
12:36:41 MUTEXX PARS RCONTEXT END MUTEXX 7191 ms
12:36:41 MUTEXX PARS CHKPTREE END MUTEXX 17 ms
12:36:42 MUTEXX PARS AST END MUTEXX 361 ms
+-----------+-------------+--+---+---------- data ------------+- result -+
09:55:51.994PARS MULANGTC10 START LEXER
lanarimarco marked this conversation as resolved.
Show resolved Hide resolved
09:55:51.994PARS MULANGTC10 END LEXER
09:55:51.994PARS MULANGTC10 START PARSER
09:55:51.994PARS MULANGTC10 END PARSER
09:55:51.995PARS MULANGTC10 START RCONTEXT
09:55:52.074PARS MULANGTC10 END RCONTEXT
09:55:52.074PARS MULANGTC10 START CHKPTREE
09:55:52.074PARS MULANGTC10 END CHKPTREE
09:55:52.074PARS MULANGTC10 START AST
09:55:52.078PARS MULANGTC10 END AST
+--------- header ----------+----------+---- parse ----+
```


Expand All @@ -235,20 +221,28 @@ to resolve a program. When the interpreter starts, the RESL channel logs the lis
of strategies used to locate a RPG/Java program.

```
15:09:46.910 RESL resource: /
15:09:46.910 RESL directory: .
15:09:46.960 TEST_06 80 RESL CALL "CALCFIB"
+-----------+-------------+--+---+---------- resolution -----------+
15:47:40.824 RESL T10_A60_P02 8 CALL "MULANGTC10"
lanarimarco marked this conversation as resolved.
Show resolved Hide resolved
+------------ header -------------+---- resolution ----+
lanarimarco marked this conversation as resolved.
Show resolved Hide resolved
```

## Analytics Channel ANALYTICS
The analytics channel provides processed collected during the whole execution cycle. Information yield by this channel are derived by a LoggingContext where logging metadata are stored.

```
10:24:41.060 ANALYTICS MUTE10_10 STMT TIME DO 451457us 33% 1 avg. 451457us
lanarimarco marked this conversation as resolved.
Show resolved Hide resolved
10:24:41.060 ANALYTICS MUTE10_10 STMT TIME EVAL 5307us 0% 2 avg. 2653us
10:24:41.061 ANALYTICS MUTE10_10 STMT TIME ZADD 243199us 18% 200000 avg. 1us
10:24:41.061 ANALYTICS MUTE10_10 STMT TIME SUBDUR 1122us 0% 1 avg. 1122us
10:24:41.065 ANALYTICS MUTE10_10 EXPR TIME 64627us 5% 1300104 avg. 0us
10:24:41.066 ANALYTICS MUTE10_10 LOG TIME 80529us 6% 1300120 avg. 0us
```

## Error Channel ERR
The error channel catches the error events (instances of `com.smeup.rpgparser.execution.ErrorEvent`).
These events are particularly meaningful during the program syntax checking, below we can see an example.
As you can see, the `ErrorEvent` is shown through its string representation.
```
12:24:28.735 ERROR02 6 ERR ErrorEvent(error=java.lang.IllegalStateException: token recognition error at: 'C ', errorEventSource=Parser, absoluteLine=6, sourceReference=SourceReference(sourceReferenceType=Program, sourceId=ERROR02, relativeLine=6, position=Position(start=Line 6, Column 6, end=Line 6, Column 6)), fragment= C EVAL x = 1 / n)
12:24:28.739 ERROR02 7 ERR ErrorEvent(error=java.lang.IllegalStateException: missing FREE_SEMI at 'C', errorEventSource=Parser, absoluteLine=7, sourceReference=SourceReference(sourceReferenceType=Program, sourceId=ERROR02, relativeLine=7, position=Position(start=Line 7, Column 5, end=Line 7, Column 5)), fragment= C SETON LR)
+-----------+-------------+--+---+---------- error ----------------+
10:05:03.612 ERR T10_A60_P02 4 ErrorEvent(error=java.lang.IllegalStateException: token recognition error at: 'Error', errorEventSource=Parser, absoluteLine=4, sourceReference=SourceReference(sourceReferenceType=Program, sourceId=T10_A60_P02, relativeLine=4, position=Position(start=Line 4, Column 5, end=Line 4, Column 5)), fragment= Error)
```

For further information about the `ErrorEvent` see the kotlin-doc in [Configuration.kt](../rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/execution/Configuration.kt)
Expand Down
Expand Up @@ -24,6 +24,7 @@
import com.smeup.rpgparser.interpreter.StringValue;
import com.smeup.rpgparser.interpreter.Value;
import com.smeup.rpgparser.jvminterop.JavaSystemInterface;
import com.smeup.rpgparser.logging.LogChannel;
import com.smeup.rpgparser.logging.LoggingKt;
import com.smeup.rpgparser.rpginterop.DirRpgProgramFinder;
import com.smeup.rpgparser.rpginterop.RpgProgramFinder;
Expand All @@ -46,7 +47,7 @@ public static CommandLineParms execPgm(String name, CommandLineParms inputParams
File srcDir = new File(Objects.requireNonNull(CallJarikoWithParams.class.getResource("/rpg")).getPath());
List<RpgProgramFinder> programFinders = List.of(new DirRpgProgramFinder(srcDir));
final JavaSystemInterface systemInterface = new JavaSystemInterface(configuration);
systemInterface.setLoggingConfiguration(LoggingKt.consoleLoggingConfiguration(LoggingKt.RESOLUTION_LOGGER, LoggingKt.PERFORMANCE_LOGGER));
systemInterface.setLoggingConfiguration(LoggingKt.consoleLoggingConfiguration(LogChannel.RESOLUTION, LogChannel.PERFORMANCE));
CommandLineProgram program = RunnerKt.getProgram(name, systemInterface, programFinders);
return program.singleCall(inputParams, configuration);
}
Expand Down
Expand Up @@ -155,7 +155,7 @@ data class JarikoCallback(
// logging configuration, the error event must be shown as before, else we run the risk to miss very helpful information
MainExecutionContext.getSystemInterface()?.apply {
if (getAllLogHandlers().isErrorChannelConfigured()) {
MainExecutionContext.log(ErrorEventLogEntry(errorEvent = errorEvent))
MainExecutionContext.log(LazyLogEntry.produceError(errorEvent))
} else {
System.err.println(errorEvent)
}
Expand Down
Expand Up @@ -19,6 +19,7 @@ package com.smeup.rpgparser.execution
import com.smeup.dbnative.manager.DBFileFactory
import com.smeup.rpgparser.experimental.ExperimentalFeaturesFactory
import com.smeup.rpgparser.interpreter.*
import com.smeup.rpgparser.logging.LoggingContext
import com.smeup.rpgparser.parsing.facade.CopyBlocks
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
Expand Down Expand Up @@ -101,6 +102,11 @@ object MainExecutionContext {
* */
fun getAttributes(): MutableMap<String, Any> = context.get()?.attributes ?: noContextAttributes

/**
* @return logging context
*/
fun getLoggingContext() = context.get()?.logging

/**
* @return a new unique identifier
*/
Expand Down Expand Up @@ -165,8 +171,8 @@ object MainExecutionContext {
/**
* Logs entries
*/
fun log(logEntry: LogEntry) {
context.get()?.log(logEntry)
fun log(renderer: LazyLogEntry) {
context.get()?.renderLog(renderer)
}

/***
Expand Down Expand Up @@ -194,6 +200,7 @@ data class Context(
val attributes: MutableMap<String, Any> = mutableMapOf<String, Any>(),
val idProvider: AtomicInteger = AtomicInteger(),
val configuration: Configuration,
val logging: LoggingContext = LoggingContext(),
val memorySliceMgr: MemorySliceMgr? = null,
val programStack: Stack<RpgProgram> = Stack<RpgProgram>(),
val systemInterface: SystemInterface,
Expand All @@ -209,8 +216,8 @@ data class Context(
systemInterface.getAllLogHandlers()
}

fun log(logEntry: LogEntry) {
logHandlers.log(logEntry)
fun renderLog(renderer: LazyLogEntry) {
logHandlers.renderLog(renderer)
}
}

Expand Down
Expand Up @@ -156,7 +156,8 @@ fun getProgram(
if (systemInterface is JavaSystemInterface) {
systemInterface.rpgSystem.addProgramFinders(programFinders)
programFinders.forEach {
systemInterface.getAllLogHandlers().log(RpgProgramFinderLogEntry(it.toString()))
val logSource = LogSourceData(it.toString(), "")
systemInterface.getAllLogHandlers().renderLog(LazyLogEntry.produceResolution(logSource))
}
} else {
// for compatibility with other system interfaces using singleton instance
Expand Down
Expand Up @@ -33,7 +33,7 @@ interface InterpreterCore {
fun getKlists(): HashMap<String, List<String>>
fun getGlobalSymbolTable(): ISymbolTable
fun getLocalizationContext(): LocalizationContext
fun log(logEntry: () -> LogEntry)
fun renderLog(producer: () -> LazyLogEntry)
fun assign(target: AssignableExpression, value: Value): Value
fun assign(dataDefinition: AbstractDataDefinition, value: Value): Value
fun assign(
Expand Down
Expand Up @@ -67,7 +67,7 @@ class SymbolTable : ISymbolTable {
it.name.equals(dataName, ignoreCase = true) && it.canBeUsedUnqualified()
}
if (field != null) {
return if (topLevelValue.key.type is ArrayValue) {
return if (topLevelValue.key.type is ArrayType) {
TODO("We do not yet handle top level values of array type")
} else {
(topLevelValue.value as DataStructValue)[field]
Expand Down
Expand Up @@ -47,6 +47,7 @@ abstract class AbstractDataDefinition(
* true means this is a constant, default false
* */
@Transient open val const: Boolean = false,
@Transient open val static: Boolean = false,
/**
* This scope. Default: got by current parsing entity
* */
Expand All @@ -63,8 +64,7 @@ abstract class AbstractDataDefinition(
if (parsingFunction != null) {
if (static) Scope.static(parsingFunction) else Scope.Local
} else Scope.Program
},
@Transient open val static: Boolean = false
}
) : Node(position), Named {
fun numberOfElements() = type.numberOfElements()
open fun elementSize() = type.elementSize()
Expand Down