Skip to content

Commit 72badad

Browse files
committed
Merged PR posit-dev/positron-python#115: fix ipkernel logging
Merge pull request #115 from posit-dev/fix-logging fix ipkernel logging -------------------- Commit message for posit-dev/positron-python@3922350: fix ipkernel logging The root logger wasn't configured to use IPKernelApp's handlers, and the `logfile` argument was being passed to `initialize` which overwrote the log file we wanted with a log of the the executed code inputs. Also switched to per-module named loggers instead of root -- just lets us easily see where a log originated from. Authored-by: Wasim Lorgat <[email protected]> Signed-off-by: Wasim Lorgat <[email protected]>
1 parent f6de7ad commit 72badad

File tree

6 files changed

+79
-59
lines changed

6 files changed

+79
-59
lines changed

extensions/positron-python/pythonFiles/positron/environment.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
from .inspectors import get_inspector, MAX_ITEMS
1212
from .utils import get_qualname
1313

14+
logger = logging.getLogger(__name__)
15+
1416

1517
@enum.unique
1618
class EnvironmentMessageType(str, enum.Enum):
@@ -271,7 +273,7 @@ def _send_message(self, msg: EnvironmentMessage) -> None:
271273
Send a message through the environment comm to the client.
272274
"""
273275
if self.env_comm is None:
274-
logging.warning("Cannot send message, environment comm is not open")
276+
logger.warning("Cannot send message, environment comm is not open")
275277
return
276278

277279
self.env_comm.send(msg)
@@ -517,7 +519,7 @@ def _summarize_variable(self, key: Any, value: Any) -> Optional[EnvironmentVaria
517519
)
518520

519521
except Exception as err:
520-
logging.warning(err, exc_info=True)
522+
logger.warning(err, exc_info=True)
521523
return EnvironmentVariable(
522524
display_name=str(key),
523525
display_value=get_qualname(value),

extensions/positron-python/pythonFiles/positron/inspectors.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
# conditional property lookup
2424
__POSITRON_DEFAULT__ = object()
2525

26+
logger = logging.getLogger(__name__)
27+
2628
#
2729
# Base inspector
2830
#
@@ -156,7 +158,7 @@ def has_child(self, value: Any, child_name: str) -> bool:
156158
index = int(child_name)
157159
return index < self.get_length(value)
158160
except Exception:
159-
logging.warning(f"Unable to find child value at '{child_name}'", exc_info=True)
161+
logger.warning(f"Unable to find child value at '{child_name}'", exc_info=True)
160162

161163
return False
162164

@@ -168,7 +170,7 @@ def get_child(self, value: Any, child_name: str) -> Any:
168170
index = int(child_name)
169171
child_value = value[index]
170172
except Exception:
171-
logging.warning(f"Unable to find child value at '{child_name}'", exc_info=True)
173+
logger.warning(f"Unable to find child value at '{child_name}'", exc_info=True)
172174

173175
return child_value
174176

@@ -371,7 +373,7 @@ def get_column(self, value: Any, column_name: str) -> Any:
371373
values = column.values.tolist()
372374
except Exception:
373375
values = []
374-
logging.warning("Unable to get Pandas column: %s", column_name, exc_info=True)
376+
logger.warning("Unable to get Pandas column: %s", column_name, exc_info=True)
375377

376378
return values
377379

@@ -395,7 +397,7 @@ def get_column_display_type(self, value: Any, column_name: str) -> str:
395397
display_type = f"{column_type} [{size}]"
396398
except Exception:
397399
display_type = ""
398-
logging.warning("Unable to get Pandas column type: %s", column_name, exc_info=True)
400+
logger.warning("Unable to get Pandas column type: %s", column_name, exc_info=True)
399401

400402
return display_type
401403

@@ -432,7 +434,7 @@ def get_display_value(
432434
display_value = value.to_string(index=False, max_rows=MAX_ITEMS)
433435
return (display_value, True)
434436
except Exception:
435-
logging.warning("Unable to display Pandas Series", exc_info=True)
437+
logger.warning("Unable to display Pandas Series", exc_info=True)
436438
display_value = self.get_display_type(value)
437439
return (display_value, True)
438440

@@ -451,7 +453,7 @@ def has_child(self, value: Any, child_name: str) -> bool:
451453
index = int(child_name)
452454
return index < self.get_length(value)
453455
except Exception:
454-
logging.warning(f"Unable to find Pandas Series child at '{child_name}'", exc_info=True)
456+
logger.warning(f"Unable to find Pandas Series child at '{child_name}'", exc_info=True)
455457

456458
return False
457459

@@ -462,7 +464,7 @@ def get_child(self, value: Any, child_name: str) -> Any:
462464
index = int(child_name)
463465
child_value = value.iat[index]
464466
except Exception:
465-
logging.warning(f"Unable to find Pandas Series child at '{child_name}'", exc_info=True)
467+
logger.warning(f"Unable to find Pandas Series child at '{child_name}'", exc_info=True)
466468

467469
return child_value
468470

@@ -481,7 +483,7 @@ def summarize_children(
481483
if summary is not None:
482484
children.append(summary)
483485
except Exception:
484-
logging.warning("Error summarizing Pandas Series children", exc_info=True)
486+
logger.warning("Error summarizing Pandas Series children", exc_info=True)
485487

486488
return children
487489

@@ -536,7 +538,7 @@ def get_column(self, value: Any, child_name: str) -> Any:
536538
column = value.get_column(child_name)
537539
return column.to_list()
538540
except Exception:
539-
logging.warning("Unable to get Polars child: %s", child_name, exc_info=True)
541+
logger.warning("Unable to get Polars child: %s", child_name, exc_info=True)
540542
return []
541543

542544
def get_column_display_type(self, value: Any, column_name: str) -> str:
@@ -558,7 +560,7 @@ def get_column_display_type(self, value: Any, column_name: str) -> str:
558560

559561
display_type = f"{column_type} [{size}]"
560562
except Exception:
561-
logging.warning("Unable to get Polars column type: %s", column_name, exc_info=True)
563+
logger.warning("Unable to get Polars column type: %s", column_name, exc_info=True)
562564
display_type = ""
563565

564566
return display_type
@@ -602,7 +604,7 @@ def get_display_value(
602604
True,
603605
)
604606
except Exception:
605-
logging.warning("Unable to display Ndarray", exc_info=True)
607+
logger.warning("Unable to display Ndarray", exc_info=True)
606608
return (self.get_display_type(value), True)
607609

608610
def get_display_type(self, value: Any) -> str:
@@ -637,7 +639,7 @@ def has_child(self, value: Any, child_name: str) -> bool:
637639
index = int(child_name)
638640
return index < self.get_length(value)
639641
except Exception:
640-
logging.warning(f"Unable to find Pandas Series child at '{child_name}'", exc_info=True)
642+
logger.warning(f"Unable to find Pandas Series child at '{child_name}'", exc_info=True)
641643
return False
642644

643645
def get_child(self, value: Any, child_name: str) -> Any:
@@ -652,7 +654,7 @@ def get_child(self, value: Any, child_name: str) -> Any:
652654

653655
return child_value
654656
except Exception:
655-
logging.warning("Unable to get ndarray child: %s", child_name, exc_info=True)
657+
logger.warning("Unable to get ndarray child: %s", child_name, exc_info=True)
656658
return []
657659

658660
def summarize_children(
@@ -670,7 +672,7 @@ def summarize_children(
670672
if summary is not None:
671673
children.append(summary)
672674
except Exception:
673-
logging.warning("Error summarizing Numpy ndarray children", exc_info=True)
675+
logger.warning("Error summarizing Numpy ndarray children", exc_info=True)
674676

675677
return children
676678

@@ -681,7 +683,7 @@ def equals(self, value1: Any, value2: Any) -> bool:
681683

682684
return np.array_equal(value1, value2)
683685
except Exception as err:
684-
logging.warning("numpy equals %s", err, exc_info=True)
686+
logger.warning("numpy equals %s", err, exc_info=True)
685687

686688
# Fallback to comparing the raw bytes
687689
if value1.shape != value2.shape:

extensions/positron-python/pythonFiles/positron/plots.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
from IPython.core.interactiveshell import InteractiveShell
1313

14+
logger = logging.getLogger(__name__)
15+
1416

1517
class PlotClientMessageImage(dict):
1618
"""
@@ -107,7 +109,7 @@ def receive_message(self, msg) -> None:
107109

108110
figure_comm = self.comms.get(comm_id, None)
109111
if figure_comm is None:
110-
logging.warning(f"Plot figure comm {comm_id} not found")
112+
logger.warning(f"Plot figure comm {comm_id} not found")
111113
return
112114

113115
pickled = self.figures.get(comm_id, None)

extensions/positron-python/pythonFiles/positron/positron_ipkernel.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def handle_pre_execute(self) -> None:
9999
try:
100100
self.snapshot_user_ns()
101101
except Exception:
102-
logging.warning("Failed to snapshot user namespace", exc_info=True)
102+
logger.warning("Failed to snapshot user namespace", exc_info=True)
103103

104104
def handle_post_execute(self) -> None:
105105
"""
@@ -117,7 +117,7 @@ def handle_post_execute(self) -> None:
117117
assigned, removed = self.compare_user_ns()
118118
self.env_service.send_update(assigned, removed)
119119
except Exception as err:
120-
logging.warning(err, exc_info=True)
120+
logger.warning(err, exc_info=True)
121121

122122
def get_user_ns(self) -> dict:
123123
return self.shell.user_ns or {} # type: ignore
@@ -200,7 +200,7 @@ def compare_user_ns(self) -> Tuple[dict, set]:
200200
assigned[key] = v2
201201

202202
except Exception as err:
203-
logging.warning("err: %s", err, exc_info=True)
203+
logger.warning("err: %s", err, exc_info=True)
204204

205205
return assigned, removed
206206

@@ -317,7 +317,7 @@ def delete_vars(self, names: Iterable, parent) -> Tuple[dict, set]:
317317
try:
318318
self.shell.del_var(name, False) # type: ignore
319319
except Exception:
320-
logging.warning(f"Unable to delete variable '{name}'")
320+
logger.warning(f"Unable to delete variable '{name}'")
321321
pass
322322

323323
assigned, removed = self.compare_user_ns()

extensions/positron-python/pythonFiles/positron/positron_jedilsp.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ def positron_completion(
277277
]
278278
except ValueError:
279279
# Ignore LSP errors for completions from invalid line/column ranges.
280-
logging.info("LSP completion error", exc_info=True)
280+
logger.info("LSP completion error", exc_info=True)
281281
completion_items = []
282282

283283
return CompletionList(is_incomplete=False, items=completion_items) if completion_items else None
@@ -329,7 +329,7 @@ def positron_hover(
329329
return hover(server, params)
330330
except ValueError:
331331
# Ignore LSP errors for hover over invalid line/column ranges.
332-
logging.info("LSP hover error", exc_info=True)
332+
logger.info("LSP hover error", exc_info=True)
333333

334334
return None
335335

@@ -378,7 +378,7 @@ def positron_code_action(
378378
return code_action(server, params)
379379
except ValueError:
380380
# Ignore LSP errors for actions with invalid line/column ranges.
381-
logging.info("LSP codeAction error", exc_info=True)
381+
logger.info("LSP codeAction error", exc_info=True)
382382

383383
return None
384384

extensions/positron-python/pythonFiles/positron_language_server.py

Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,7 @@
2121
logger = logging.getLogger(__name__)
2222

2323

24-
def initialize_config() -> None:
25-
"""
26-
Initialize the configuration for the Positron Python Language Server
27-
and REPL Kernel.
28-
"""
29-
24+
def parse_args() -> argparse.Namespace:
3025
# Given we're using TCP, support a subset of the Jedi LSP configuration
3126
parser = argparse.ArgumentParser(
3227
prog="positron-language-server",
@@ -54,27 +49,21 @@ def initialize_config() -> None:
5449
)
5550
parser.add_argument(
5651
"-f",
57-
help="location of the IPyKernel configuration file",
52+
"--connection-file",
53+
help="location of the IPyKernel connection file",
5854
type=str,
5955
)
60-
parser.add_argument(
61-
"-v",
62-
"--verbose",
63-
help="increase verbosity of log output",
64-
action="count",
65-
default=0,
66-
)
6756
args = parser.parse_args()
68-
6957
args.loglevel = args.loglevel.upper()
70-
if args.logfile:
71-
logging.basicConfig(
72-
filename=args.logfile,
73-
filemode="w",
74-
level=args.loglevel,
75-
)
76-
else:
77-
logging.basicConfig(stream=sys.stderr, level=args.loglevel)
58+
59+
return args
60+
61+
62+
if __name__ == "__main__":
63+
exit_status = 0
64+
65+
# Parse command-line arguments
66+
args = parse_args()
7867

7968
# Start the debugpy debugger if a port was specified
8069
if args.debugport is not None:
@@ -83,18 +72,43 @@ def initialize_config() -> None:
8372

8473
debugpy.listen(args.debugport)
8574
except Exception as error:
86-
logging.warning(f"Unable to start debugpy: {error}", exc_info=True)
87-
88-
89-
if __name__ == "__main__":
90-
exit_status = 0
91-
92-
# Init the configuration args
93-
initialize_config()
75+
logger.warning(f"Unable to start debugpy: {error}", exc_info=True)
76+
77+
# Configure logging by passing the IPKernelApp traitlets application by passing a logging config
78+
# dict. See: https://docs.python.org/3/library/logging.config.html#logging-config-dictschema for
79+
# more info about this schema.
80+
handlers = ["console"] if args.logfile is None else ["file"]
81+
logging_config = {
82+
"loggers": {
83+
"root": {
84+
"level": args.loglevel,
85+
"handlers": handlers,
86+
},
87+
"IPKernelApp": {
88+
"handlers": handlers,
89+
},
90+
}
91+
}
92+
if args.logfile is not None:
93+
logging_config["handlers"] = {
94+
"file": {
95+
"class": "logging.FileHandler",
96+
"formatter": "console",
97+
"level": args.loglevel,
98+
"filename": args.logfile,
99+
}
100+
}
94101

95102
# Start Positron's IPyKernel as the interpreter for our console.
96-
app = kernelapp.IPKernelApp.instance(kernel_class=PositronIPyKernel)
97-
app.initialize()
103+
app = kernelapp.IPKernelApp.instance(
104+
kernel_class=PositronIPyKernel,
105+
connection_file=args.connection_file,
106+
log_level=args.loglevel,
107+
logging_config=logging_config,
108+
)
109+
# Initialize with empty argv, otherwise BaseIPythonApplication.initialize reuses our
110+
# command-line arguments in unexpected ways (e.g. logfile instructs it to log executed code).
111+
app.initialize(argv=[])
98112
app.kernel.start()
99113

100114
logger.info(f"Process ID {os.getpid()}")
@@ -104,7 +118,7 @@ def initialize_config() -> None:
104118
loop = asyncio.get_event_loop()
105119

106120
# Enable asyncio debug mode.
107-
if logging.getLogger().level == logging.DEBUG:
121+
if args.loglevel == "DEBUG":
108122
loop.set_debug(True)
109123

110124
try:

0 commit comments

Comments
 (0)