forked from microsoft/vscode-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path__main__.py
More file actions
126 lines (106 loc) · 3.77 KB
/
__main__.py
File metadata and controls
126 lines (106 loc) · 3.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import argparse
import importlib
import json
import logging
import logging.config
import sys
log = logging.getLogger(__name__)
LOG_FORMAT = "%(asctime)s UTC - %(levelname)s - %(name)s - %(message)s"
queue_handler = None
def add_arguments(parser):
parser.description = "Daemon"
parser.add_argument(
"--daemon-module",
default="datascience.daemon.daemon_python",
help="Daemon Module",
)
log_group = parser.add_mutually_exclusive_group()
log_group.add_argument(
"--log-config", help="Path to a JSON file containing Python logging config."
)
log_group.add_argument(
"--log-file",
help="Redirect logs to the given file instead of writing to stderr."
"Has no effect if used with --log-config.",
)
parser.add_argument(
"-v",
"--verbose",
action="count",
default=0,
help="Increase verbosity of log output, overrides log config file",
)
class TemporaryQueueHandler(logging.Handler):
""" Logger used to temporarily store everything into a queue.
Later the messages are pushed back to the RPC client as a notification.
Once the RPC channel is up, we'll stop queuing messages and sending id directly.
"""
def __init__(self):
logging.Handler.__init__(self)
self.queue = []
self.server = None
def set_server(self, server):
# Send everything that has beeen queued until now.
self.server = server
for msg in self.queue:
self.server._endpoint.notify("log", msg)
self.queue = []
def emit(self, record):
data = {"level": record.levelname, "msg": self.format(record)}
# If we don't have the server, then queue it and send it later.
if self.server is None:
self.queue.append(data)
else:
self.server._endpoint.notify("log", data)
def _configure_logger(verbose=0, log_config=None, log_file=None):
root_logger = logging.root
global queue_handler
if log_config:
with open(log_config, "r") as f:
logging.config.dictConfig(json.load(f))
else:
formatter = logging.Formatter(LOG_FORMAT)
if log_file:
log_handler = logging.handlers.RotatingFileHandler(
log_file,
mode="a",
maxBytes=50 * 1024 * 1024,
backupCount=10,
encoding=None,
delay=0,
)
log_handler.setFormatter(formatter)
root_logger.addHandler(log_handler)
else:
queue_handler = TemporaryQueueHandler()
root_logger.addHandler(queue_handler)
if verbose == 0:
level = logging.WARNING
elif verbose == 1:
level = logging.INFO
elif verbose >= 2:
level = logging.DEBUG
root_logger.setLevel(level)
def main():
""" Starts the daemon.
The daemon_module allows authors of modules to provide a custom daemon implementation.
E.g. we have a base implementation for standard python functionality,
and a custom daemon implementation for DS work (related to jupyter).
"""
parser = argparse.ArgumentParser()
add_arguments(parser)
args = parser.parse_args()
_configure_logger(args.verbose, args.log_config, args.log_file)
log.info("Starting daemon from %s.PythonDaemon", args.daemon_module)
try:
daemon_module = importlib.import_module(args.daemon_module)
daemon_cls = daemon_module.PythonDaemon
daemon_cls.start_daemon(queue_handler)
except Exception:
import traceback
log.error(traceback.format_exc())
raise Exception("Failed to start daemon")
if __name__ == "__main__":
main()