Skip to content

Commit 31e655c

Browse files
committed
feat: add %log start|off|on|state|level|stop for kernel logging
1 parent 8ccda13 commit 31e655c

2 files changed

Lines changed: 227 additions & 1 deletion

File tree

jjava-distro/src/main/java/org/dflib/jjava/distro/JJava.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.dflib.jjava.kernel.magics.ClasspathMagic;
88
import org.dflib.jjava.kernel.magics.JarsMagic;
99
import org.dflib.jjava.kernel.magics.LoadCodeMagic;
10+
import org.dflib.jjava.kernel.magics.LoggingMagic;
1011
import org.dfllib.jjava.maven.MavenDependencyResolver;
1112
import org.dfllib.jjava.maven.magics.AddMavenDependencyMagic;
1213
import org.dfllib.jjava.maven.magics.LoadFromPomCellMagic;
@@ -58,8 +59,10 @@ public static void main(String[] args) throws Exception {
5859
.extraClasspath(Env.extraClasspath())
5960
.timeout(Env.timeout())
6061

62+
.lineMagic("log", new LoggingMagic())
6163
.lineMagic("load", new LoadCodeMagic("", ".jsh", ".jshell", ".java", ".jjava"))
6264
.lineMagic("classpath", new ClasspathMagic())
65+
// IPython-compatible logging commands
6366
.lineMagic("maven", new MavenMagic(mavenResolver))
6467
.lineMagic("mavenRepo", new MavenRepoMagic(mavenResolver))
6568
.lineMagic("loadFromPOM", new LoadFromPomLineMagic(mavenResolver))
@@ -69,7 +72,6 @@ public static void main(String[] args) throws Exception {
6972
.lineMagic("addMavenDependency", new AddMavenDependencyMagic(mavenResolver))
7073

7174
.cellMagic("loadFromPOM", new LoadFromPomCellMagic(mavenResolver))
72-
7375
.build();
7476

7577
kernel.becomeHandlerForConnection(connection);
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
package org.dflib.jjava.kernel.magics;
2+
3+
import org.dflib.jjava.jupyter.kernel.magic.LineMagic;
4+
import org.dflib.jjava.kernel.JavaKernel;
5+
6+
import java.io.IOException;
7+
import java.util.List;
8+
import java.util.logging.ConsoleHandler;
9+
import java.util.logging.FileHandler;
10+
import java.util.logging.Handler;
11+
import java.util.logging.Level;
12+
import java.util.logging.Logger;
13+
import java.util.logging.SimpleFormatter;
14+
15+
/**
16+
* Magic command to control Java logging for the kernel.
17+
*
18+
* Usage:
19+
* %log start [filename] - Start logging to file (default: <kernelname>.log)
20+
* %log stop - Stop logging and close log file
21+
* %log on - Resume logging (if paused)
22+
* %log off - Pause logging (but keep file open)
23+
* %log state - Show current logging status
24+
* %log level <LEVEL> - Set logging level (SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST)
25+
*/
26+
public class LoggingMagic implements LineMagic<Void, JavaKernel> {
27+
28+
Logger log = Logger.getLogger(LoggingMagic.class.getName());
29+
30+
private static final Logger logger = Logger.getLogger(LoggingMagic.class.getName());
31+
private static FileHandler fileHandler;
32+
private static boolean loggingEnabled = false;
33+
private static boolean loggingPaused = false;
34+
private static Level currentLevel = Level.INFO;
35+
private static String currentLogFile = null;
36+
37+
@Override
38+
public Void eval(JavaKernel kernel, List<String> args) throws Exception {
39+
if (args.isEmpty()) {
40+
System.out.println(getUsage());
41+
return null;
42+
}
43+
44+
String command = args.get(0).toLowerCase();
45+
46+
switch (command) {
47+
case "start":
48+
String filename = args.size() > 1 ? args.get(1) : getLogFileName(kernel);
49+
System.out.println(startLogging(kernel, filename));
50+
log.info("Logging started. Log file: " + filename + " (Level: " + currentLevel + ")");
51+
return null;
52+
case "stop":
53+
System.out.println(stopLogging());
54+
return null;
55+
case "on":
56+
System.out.println(resumeLogging());
57+
return null;
58+
case "off":
59+
System.out.println(pauseLogging());
60+
return null;
61+
case "state":
62+
System.out.println(getLoggingStatus(kernel));
63+
return null;
64+
case "level":
65+
if (args.size() < 2) {
66+
System.out.println("Error: Please specify a logging level (SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST)");
67+
return null;
68+
}
69+
System.out.println(setLoggingLevel(args.get(1)));
70+
return null;
71+
default:
72+
System.out.println(getUsage());
73+
return null;
74+
}
75+
}
76+
77+
78+
private String setLoggingLevel(String levelName) {
79+
try {
80+
Level newLevel = Level.parse(levelName.toUpperCase());
81+
currentLevel = newLevel;
82+
83+
if (fileHandler != null) {
84+
fileHandler.setLevel(newLevel);
85+
Logger rootLogger = Logger.getLogger("");
86+
rootLogger.setLevel(newLevel);
87+
}
88+
89+
return "Logging level set to: " + newLevel;
90+
} catch (IllegalArgumentException e) {
91+
return "Error: Invalid logging level '" + levelName + "'. Valid levels: SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST";
92+
}
93+
}
94+
95+
private String getLoggingStatus(JavaKernel kernel) {
96+
StringBuilder status = new StringBuilder();
97+
status.append("Logging Status:\n");
98+
status.append(" Active: ").append(loggingEnabled ? "Yes" : "No").append("\n");
99+
100+
if (loggingEnabled) {
101+
status.append(" Paused: ").append(loggingPaused ? "Yes" : "No").append("\n");
102+
status.append(" Log file: ").append(currentLogFile != null ? currentLogFile : getLogFileName(kernel)).append("\n");
103+
}
104+
105+
status.append(" Level: ").append(currentLevel);
106+
107+
return status.toString();
108+
}
109+
110+
private String getLogFileName(JavaKernel kernel) {
111+
String kernelName = kernel.getName().toLowerCase();
112+
return kernelName + ".log";
113+
}
114+
115+
private String startLogging(JavaKernel kernel, String filename) {
116+
if (loggingEnabled && !loggingPaused) {
117+
return "Logging is already active. Log file: " + currentLogFile;
118+
}
119+
120+
try {
121+
currentLogFile = filename;
122+
fileHandler = new FileHandler(filename, true); // append mode
123+
fileHandler.setFormatter(new SimpleFormatter());
124+
fileHandler.setLevel(currentLevel);
125+
126+
// Get root logger and configure it
127+
Logger rootLogger = Logger.getLogger("");
128+
129+
// Remove all existing handlers (including console handlers)
130+
Handler[] existingHandlers = rootLogger.getHandlers();
131+
for (Handler handler : existingHandlers) {
132+
rootLogger.removeHandler(handler);
133+
}
134+
135+
// Add only our file handler
136+
rootLogger.addHandler(fileHandler);
137+
rootLogger.setLevel(currentLevel);
138+
139+
// Also set level for our specific logger
140+
logger.setLevel(currentLevel);
141+
142+
loggingEnabled = true;
143+
loggingPaused = false;
144+
return "Logging started. Log file: " + filename + " (Level: " + currentLevel + ")";
145+
} catch (IOException e) {
146+
return "Error starting logging: " + e.getMessage();
147+
}
148+
}
149+
150+
private String stopLogging() {
151+
if (!loggingEnabled) {
152+
return "Logging is not active";
153+
}
154+
155+
if (fileHandler != null) {
156+
Logger rootLogger = Logger.getLogger("");
157+
rootLogger.removeHandler(fileHandler);
158+
fileHandler.close();
159+
fileHandler = null;
160+
}
161+
162+
loggingEnabled = false;
163+
loggingPaused = false;
164+
String logFile = currentLogFile;
165+
currentLogFile = null;
166+
return "Logging stopped. Log file was: " + logFile;
167+
}
168+
169+
private String pauseLogging() {
170+
if (!loggingEnabled) {
171+
return "Logging is not active";
172+
}
173+
174+
if (loggingPaused) {
175+
return "Logging is already paused";
176+
}
177+
178+
// Temporarily disable the file handler
179+
if (fileHandler != null) {
180+
fileHandler.setLevel(Level.OFF);
181+
}
182+
183+
loggingPaused = true;
184+
return "Logging paused. Log file: " + currentLogFile;
185+
}
186+
187+
private String resumeLogging() {
188+
if (!loggingEnabled) {
189+
return "Logging is not active. Use %log start to start logging.";
190+
}
191+
192+
if (!loggingPaused) {
193+
return "Logging is already active";
194+
}
195+
196+
// Re-enable the file handler
197+
if (fileHandler != null) {
198+
fileHandler.setLevel(currentLevel);
199+
}
200+
201+
loggingPaused = false;
202+
return "Logging resumed. Log file: " + currentLogFile + " (Level: " + currentLevel + ")";
203+
}
204+
205+
private String getUsage() {
206+
return "Usage: %log <command>\n" +
207+
"\nCommands:\n" +
208+
" start [filename] - Start logging to file (default: <kernelname>.log)\n" +
209+
" stop - Stop logging and close log file\n" +
210+
" on - Resume logging (if paused)\n" +
211+
" off - Pause logging (but keep file open)\n" +
212+
" state - Show current logging status\n" +
213+
" level <LEVEL> - Set logging level (SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST)\n" +
214+
"\nExamples:\n" +
215+
" %log start - Start logging to <kernelname>.log\n" +
216+
" %log start myapp.log - Start logging to myapp.log\n" +
217+
" %log off - Pause logging\n" +
218+
" %log on - Resume logging\n" +
219+
" %log state - Check status\n" +
220+
" %log level WARNING - Set logging level\n" +
221+
" %log stop - Stop and close log file";
222+
}
223+
224+
}

0 commit comments

Comments
 (0)