Skip to content

Commit 770a41d

Browse files
committed
updated html logger
- now saves variables to separate independent html - now does not need screenshot folder path
1 parent 765c082 commit 770a41d

File tree

10 files changed

+970
-368
lines changed

10 files changed

+970
-368
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ configure(allprojects){
2525
testNgVersion = '6.14.3'
2626
loggerVersion = '2.20.0'
2727
jnaVersion = '5.3.1'
28-
version '3.0.7'
28+
version '3.1.0'
2929
}
3030
group "$groupName"
3131

src/main/java/com/automationanywhere/botcommand/actions/config/CSVReader.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,7 @@
1313
import org.apache.commons.csv.CSVParser;
1414
import org.apache.commons.csv.CSVRecord;
1515

16-
import java.io.BufferedReader;
17-
import java.io.File;
18-
import java.io.FileInputStream;
19-
import java.io.IOException;
20-
import java.io.InputStreamReader;
21-
import java.io.Reader;
16+
import java.io.*;
2217
import java.nio.charset.Charset;
2318
import java.nio.charset.StandardCharsets;
2419
import java.util.LinkedHashMap;
@@ -166,7 +161,9 @@ public DictionaryValue action(
166161

167162
// Process CSV records
168163
for (CSVRecord csvRecord : csvParser) {
169-
if (csvRecord.size() == 0) continue;
164+
if (csvRecord.size() == 0) {
165+
continue;
166+
}
170167

171168
String key = null;
172169
String value = null;

src/main/java/com/automationanywhere/botcommand/actions/config/ExcelReader.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,9 @@ public DictionaryValue action(
167167
// Process rows
168168
boolean headerSkipped = false;
169169
for (Row row : sheet) {
170-
if (row == null) continue;
170+
if (row == null) {
171+
continue;
172+
}
171173

172174
// Skip header row if using column headers
173175
if (!headerSkipped && hasHeader) {

src/main/java/com/automationanywhere/botcommand/actions/logs/LogMessage.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ public void action(
163163
message.put(CustomHTMLLayout.Columns.VARIABLES, variableValues);
164164
message.put(CustomHTMLLayout.Columns.SOURCE, getFormattedBotUri());
165165

166+
// Add variables folder path if variables are being logged
167+
if (variableValues != null && !variableValues.isEmpty()) {
168+
message.put(CustomHTMLLayout.Columns.VARIABLES_FOLDER_PATH, session.getVariablesFolderPath(log4jLevel));
169+
}
170+
166171
Logger logger = session.getLogger();
167172
switch (logLevel) {
168173
case LEVEL_INFO:
@@ -184,7 +189,9 @@ public void action(
184189

185190
/**
186191
* Converts string log level to Log4j Level object
192+
*
187193
* @param logLevel The string representation of log level
194+
*
188195
* @return The corresponding Log4j Level object
189196
*/
190197
private Level convertToLog4jLevel(String logLevel) {

src/main/java/com/automationanywhere/botcommand/utilities/logger/CustomHTMLLayout.java

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.Map;
1919
import java.util.Objects;
2020
import java.util.Optional;
21+
import java.util.UUID;
2122

2223
/**
2324
* @author Sumit Kumar
@@ -41,14 +42,13 @@ public class CustomHTMLLayout extends AbstractStringLayout {
4142
}
4243
}
4344

44-
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss Z"); // Define your desired date and time
45-
// format
45+
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss Z"); // Define your desired date and
46+
// time format
4647

4748
public CustomHTMLLayout(Charset charset) {
4849
super(charset);
4950
}
5051

51-
5252
@PluginBuilderFactory
5353
public static Builder newBuilder() {
5454
return new Builder();
@@ -59,61 +59,86 @@ public String toSerializable(LogEvent event) {
5959
String formattedContent;
6060
String message;
6161
String sourceBotPath = "";
62-
String htmlFormattedVariables = "";
63-
String screenshotPath = "";
62+
String variablesLink = "";
63+
String screenshotLink = "";
6464

65-
//parse the message and extract column details for current row
65+
// Parse the message and extract column details for current row
6666
Object[] parameters = event.getMessage().getParameters();
6767
if (parameters != null && parameters.length > 0 && parameters[ 0 ] instanceof Map) {
6868
Map<String, Object> messageObject = (Map<String, Object>) parameters[ 0 ];
6969
message = Optional.ofNullable(messageObject.get(Columns.MESSAGE)).map(Object::toString).orElse("");
7070
sourceBotPath = Optional.ofNullable(messageObject.get(Columns.SOURCE)).map(Object::toString).orElse("");
71-
screenshotPath = Optional.ofNullable(messageObject.get(Columns.SCREENSHOT)).map(Object::toString).orElse(
72-
"");
71+
String screenshotPath =
72+
Optional.ofNullable(messageObject.get(Columns.SCREENSHOT)).map(Object::toString).orElse("");
7373

74+
// Process variables if present
7475
if (messageObject.get(Columns.VARIABLES) != null && messageObject.get(Columns.VARIABLES) instanceof Map) {
7576
Map<String, Value> variableMap = (Map<String, Value>) messageObject.get(Columns.VARIABLES);
76-
htmlFormattedVariables = HTMLGenerator.generateHTML(variableMap);
77+
78+
// Generate count display for the main log
79+
String variablesCount = HTMLGenerator.generateHTML(variableMap);
80+
81+
// If the variables folder path is provided, generate a separate file
82+
if (messageObject.get("variablesFolderPath") != null) {
83+
String variablesFolderPath = messageObject.get("variablesFolderPath").toString();
84+
String logEventId = UUID.randomUUID().toString();
85+
86+
// Generate variable file and get the link
87+
String variablesFilePath = HTMLGenerator.generateVariableFile(variableMap, variablesFolderPath,
88+
logEventId);
89+
90+
if (!variablesFilePath.isEmpty()) {
91+
variablesLink =
92+
"<a href='" + variablesFilePath + "' target='_blank' class='vars-link'>" + variablesCount + "</a>";
93+
} else {
94+
variablesLink = variablesCount;
95+
}
96+
} else {
97+
variablesLink = variablesCount;
98+
}
7799
}
78100

101+
// Get screenshot link if screenshot exists
102+
screenshotLink = HTMLGenerator.getScreenshotHTML(screenshotPath);
79103
} else {
80104
message = event.getMessage().getFormattedMessage();
81105
}
82106

107+
// Get the level as a CSS class for styling
108+
String levelClass = "level-" + event.getLevel().toString();
109+
83110
formattedContent = String.format(
84111
"<tr>" +
85112
"<td>%s</td>" +
113+
"<td class='%s'>%s</td>" +
86114
"<td>%s</td>" +
87-
"<td>%s</td>" +
88-
"<td>%s</td>" +
89-
"<td>%s</td>" +
115+
"<td class='responsive-hide'>%s</td>" +
116+
"<td class='responsive-hide'>%s</td>" +
90117
"<td>%s</td>" +
91118
"<td>%s</td>" +
92119
"<td>%s</td>" +
93120
"</tr>",
94121
StringEscapeUtils.escapeHtml4(dateFormat.format(event.getTimeMillis())),
122+
levelClass,
95123
StringEscapeUtils.escapeHtml4(event.getLevel().toString()),
96124
StringEscapeUtils.escapeHtml4(sourceBotPath),
97125
StringEscapeUtils.escapeHtml4(machine),
98126
StringEscapeUtils.escapeHtml4(user),
99127
StringEscapeUtils.escapeHtml4(message),
100-
htmlFormattedVariables,
101-
HTMLGenerator.getScreenshotHTML(screenshotPath)
128+
variablesLink,
129+
screenshotLink
102130
);
103131

104-
105132
return formattedContent;
106133
}
107134

108135
@Override
109136
public byte[] getFooter() {
110-
// Add footer content similar to Log4j2's HtmlLayout
111137
return footer;
112138
}
113139

114140
@Override
115141
public byte[] getHeader() {
116-
// Add header content similar to Log4j2's HtmlLayout
117142
return header;
118143
}
119144

@@ -134,13 +159,13 @@ public Builder withCharset(Charset charset) {
134159
public CustomHTMLLayout build() {
135160
return new CustomHTMLLayout(charset);
136161
}
137-
138162
}
139163

140164
public static class Columns {
141165
public static final String SOURCE = "Source";
142166
public static final String MESSAGE = "Message";
143167
public static final String VARIABLES = "Variables";
144168
public static final String SCREENSHOT = "Screenshot";
169+
public static final String VARIABLES_FOLDER_PATH = "variablesFolderPath";
145170
}
146-
}
171+
}

src/main/java/com/automationanywhere/botcommand/utilities/logger/CustomLogger.java

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,8 @@
33
import com.automationanywhere.toolchain.runtime.session.CloseableSessionObject;
44
import org.apache.commons.io.FilenameUtils;
55
import org.apache.logging.log4j.Level;
6-
import org.apache.logging.log4j.LogManager;
76
import org.apache.logging.log4j.Logger;
8-
import org.apache.logging.log4j.core.Appender;
97
import org.apache.logging.log4j.core.LoggerContext;
10-
import org.apache.logging.log4j.core.appender.RollingFileAppender;
11-
import org.apache.logging.log4j.core.config.Configuration;
12-
import org.apache.logging.log4j.core.config.ConfigurationFactory;
13-
import org.apache.logging.log4j.core.config.ConfigurationSource;
14-
import org.apache.logging.log4j.core.config.DefaultConfiguration;
158
import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder;
169
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
1710
import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder;
@@ -39,6 +32,7 @@ public class CustomLogger implements CloseableSessionObject {
3932
private final String loggerId;
4033
private final LoggerContext loggerContext;
4134
private final Map<Level, String> screenshotFolderPaths;
35+
private final Map<Level, String> variablesFolderPaths;
4236

4337
// Constructor for a single log file for all levels
4438
public CustomLogger(String loggerName, String logFilePath, long sizeLimitMB) throws IOException {
@@ -47,15 +41,22 @@ public CustomLogger(String loggerName, String logFilePath, long sizeLimitMB) thr
4741
// Create screenshot folder at the same location as log file
4842
String baseDir = FilenameUtils.getFullPath(logFilePath);
4943
String screenshotFolder = baseDir + "screenshots";
44+
String variablesFolder = baseDir + "variables";
5045

5146
this.screenshotFolderPaths = new HashMap<>();
52-
// Use the same screenshot folder for all levels when using a combined log file
47+
this.variablesFolderPaths = new HashMap<>();
48+
49+
// Use the same folder for all levels when using a combined log file
5350
this.screenshotFolderPaths.put(Level.INFO, screenshotFolder);
5451
this.screenshotFolderPaths.put(Level.WARN, screenshotFolder);
5552
this.screenshotFolderPaths.put(Level.ERROR, screenshotFolder);
5653

57-
// Create screenshot directory
58-
createScreenshotDirectories();
54+
this.variablesFolderPaths.put(Level.INFO, variablesFolder);
55+
this.variablesFolderPaths.put(Level.WARN, variablesFolder);
56+
this.variablesFolderPaths.put(Level.ERROR, variablesFolder);
57+
58+
// Create directories
59+
createDirectories();
5960

6061
// Create a unique logger context for this instance
6162
LoggerContext context = createNewLoggerContext();
@@ -64,7 +65,8 @@ public CustomLogger(String loggerName, String logFilePath, long sizeLimitMB) thr
6465
ConfigurationBuilder<BuiltConfiguration> builder = new DefaultConfigurationBuilder();
6566
setupLoggerConfiguration(builder);
6667

67-
AppenderComponentBuilder appenderBuilder = getCustomAppenderBuilder(builder, "COMBINED_" + loggerId, logFilePath,
68+
AppenderComponentBuilder appenderBuilder = getCustomAppenderBuilder(builder, "COMBINED_" + loggerId,
69+
logFilePath,
6870
sizeLimitMB);
6971
builder.add(appenderBuilder);
7072
builder.add(builder.newLogger(loggerName, Level.INFO)
@@ -84,6 +86,20 @@ public CustomLogger(String loggerName, String logFilePath, long sizeLimitMB) thr
8486
this.logger = context.getLogger(loggerName);
8587
}
8688

89+
private void createDirectories() throws IOException {
90+
// Create screenshot directories
91+
for (String path : screenshotFolderPaths.values()) {
92+
Path directoryPath = Paths.get(path);
93+
Files.createDirectories(directoryPath);
94+
}
95+
96+
// Create variables directories
97+
for (String path : variablesFolderPaths.values()) {
98+
Path directoryPath = Paths.get(path);
99+
Files.createDirectories(directoryPath);
100+
}
101+
}
102+
87103
private LoggerContext createNewLoggerContext() {
88104
// Creating a completely separate LoggerContext
89105
return new LoggerContext("Context-" + loggerId);
@@ -122,17 +138,20 @@ private AppenderComponentBuilder getCustomAppenderBuilder(ConfigurationBuilder<B
122138
public CustomLogger(String loggerName, Map<Level, String> levelFilePathMap, long sizeLimitMB) throws IOException {
123139
this.loggerId = UUID.randomUUID().toString();
124140
this.screenshotFolderPaths = new HashMap<>();
141+
this.variablesFolderPaths = new HashMap<>();
125142

126-
// Create a screenshot folder alongside each log file
143+
// Create a screenshot and variables folder alongside each log file
127144
for (Map.Entry<Level, String> entry : levelFilePathMap.entrySet()) {
128145
Level level = entry.getKey();
129146
String filePath = entry.getValue();
130147
String baseDir = FilenameUtils.getFullPath(filePath);
148+
131149
this.screenshotFolderPaths.put(level, baseDir + "screenshots");
150+
this.variablesFolderPaths.put(level, baseDir + "variables");
132151
}
133152

134-
// Create all screenshot directories
135-
createScreenshotDirectories();
153+
// Create all directories
154+
createDirectories();
136155

137156
// Create a unique logger context for this instance
138157
LoggerContext context = createNewLoggerContext();
@@ -176,14 +195,6 @@ public CustomLogger(String loggerName, Map<Level, String> levelFilePathMap, long
176195
this.logger = context.getLogger(loggerName);
177196
}
178197

179-
private void createScreenshotDirectories() throws IOException {
180-
// Create unique directories only (remove duplicates)
181-
for (String path : screenshotFolderPaths.values()) {
182-
Path directoryPath = Paths.get(path);
183-
Files.createDirectories(directoryPath);
184-
}
185-
}
186-
187198
public Logger getLogger() {
188199
return logger;
189200
}
@@ -210,9 +221,30 @@ public String getScreenshotFolderPath(Level level) {
210221

211222
/**
212223
* Returns the screenshot folder path for the INFO level
224+
*
213225
* @return default screenshot folder path (INFO level)
214226
*/
215227
public String getScreenshotFolderPath() {
216228
return screenshotFolderPaths.get(Level.INFO);
217229
}
230+
231+
/**
232+
* Returns the variables folder path for the specified log level
233+
*
234+
* @param level Log level
235+
*
236+
* @return variables folder path for the specified level
237+
*/
238+
public String getVariablesFolderPath(Level level) {
239+
return variablesFolderPaths.getOrDefault(level, variablesFolderPaths.get(Level.INFO));
240+
}
241+
242+
/**
243+
* Returns the variables folder path for the INFO level
244+
*
245+
* @return default variables folder path (INFO level)
246+
*/
247+
public String getVariablesFolderPath() {
248+
return variablesFolderPaths.get(Level.INFO);
249+
}
218250
}

0 commit comments

Comments
 (0)